mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-16 07:18:52 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into orange
This commit is contained in:
commit
17e2140826
102 changed files with 1645 additions and 837 deletions
|
@ -1 +1,2 @@
|
|||
set(GRAPHVIZ_EXTERNAL_LIBS FALSE)
|
||||
set(GRAPHVIZ_EXTERNAL_LIBS FALSE)
|
||||
set(GRAPHVIZ_IGNORE_TARGETS "shared;networking")
|
|
@ -11,7 +11,7 @@ endif ()
|
|||
link_hifi_libraries(
|
||||
audio avatars octree gpu model fbx entities
|
||||
networking animation recording shared script-engine embedded-webserver
|
||||
controllers physics plugins
|
||||
physics plugins
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
|
|
|
@ -103,11 +103,6 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
);
|
||||
|
||||
connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);
|
||||
connect(nodeList.data(), &NodeList::nodeAdded, this, [this](const SharedNodePointer& node) {
|
||||
if (node->getType() == NodeType::DownstreamAudioMixer) {
|
||||
node->activatePublicSocket();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AudioMixer::queueAudioPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node) {
|
||||
|
@ -171,19 +166,6 @@ void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> mes
|
|||
}
|
||||
}
|
||||
|
||||
DisplayPluginList getDisplayPlugins() {
|
||||
DisplayPluginList result;
|
||||
return result;
|
||||
}
|
||||
|
||||
InputPluginList getInputPlugins() {
|
||||
InputPluginList result;
|
||||
return result;
|
||||
}
|
||||
|
||||
// must be here to satisfy a reference in PluginManager::saveSettings()
|
||||
void saveInputPluginSettings(const InputPluginList& plugins) {}
|
||||
|
||||
const std::pair<QString, CodecPluginPointer> AudioMixer::negotiateCodec(std::vector<QString> codecs) {
|
||||
QString selectedCodecName;
|
||||
CodecPluginPointer selectedCodec;
|
||||
|
@ -389,7 +371,10 @@ void AudioMixer::start() {
|
|||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// prepare the NodeList
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::DownstreamAudioMixer, NodeType::EntityScriptServer });
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet({
|
||||
NodeType::Agent, NodeType::EntityScriptServer,
|
||||
NodeType::UpstreamAudioMixer, NodeType::DownstreamAudioMixer
|
||||
});
|
||||
nodeList->linkedDataCreateCallback = [&](Node* node) { getOrCreateClientData(node); };
|
||||
|
||||
// parse out any AudioMixer settings
|
||||
|
|
|
@ -51,6 +51,11 @@ public:
|
|||
static const QVector<ReverbSettings>& getReverbSettings() { return _zoneReverbSettings; }
|
||||
static const std::pair<QString, CodecPluginPointer> negotiateCodec(std::vector<QString> codecs);
|
||||
|
||||
static bool shouldReplicateTo(const Node& from, const Node& to) {
|
||||
return to.getType() == NodeType::DownstreamAudioMixer &&
|
||||
to.getPublicSocket() != from.getPublicSocket() &&
|
||||
to.getLocalSocket() != from.getLocalSocket();
|
||||
}
|
||||
public slots:
|
||||
void run() override;
|
||||
void sendStatsPacket() override;
|
||||
|
|
|
@ -67,24 +67,25 @@ void AudioMixerClientData::processPackets() {
|
|||
case PacketType::MicrophoneAudioNoEcho:
|
||||
case PacketType::MicrophoneAudioWithEcho:
|
||||
case PacketType::InjectAudio:
|
||||
case PacketType::AudioStreamStats:
|
||||
case PacketType::SilentAudioFrame:
|
||||
case PacketType::ReplicatedMicrophoneAudioNoEcho:
|
||||
case PacketType::ReplicatedMicrophoneAudioWithEcho:
|
||||
case PacketType::ReplicatedInjectAudio:
|
||||
case PacketType::ReplicatedSilentAudioFrame: {
|
||||
case PacketType::SilentAudioFrame: {
|
||||
|
||||
if (node->isUpstream() && !_hasSetupCodecForUpstreamNode) {
|
||||
if (node->isUpstream()) {
|
||||
setupCodecForReplicatedAgent(packet);
|
||||
}
|
||||
|
||||
QMutexLocker lock(&getMutex());
|
||||
parseData(*packet);
|
||||
|
||||
|
||||
optionallyReplicatePacket(*packet, *node);
|
||||
|
||||
break;
|
||||
}
|
||||
case PacketType::AudioStreamStats: {
|
||||
QMutexLocker lock(&getMutex());
|
||||
parseData(*packet);
|
||||
|
||||
break;
|
||||
}
|
||||
case PacketType::NegotiateAudioFormat:
|
||||
negotiateAudioFormat(*packet, node);
|
||||
break;
|
||||
|
@ -141,7 +142,7 @@ void AudioMixerClientData::optionallyReplicatePacket(ReceivedMessage& message, c
|
|||
|
||||
// enumerate the downstream audio mixers and send them the replicated version of this packet
|
||||
nodeList->unsafeEachNode([&](const SharedNodePointer& downstreamNode) {
|
||||
if (downstreamNode->getType() == NodeType::DownstreamAudioMixer) {
|
||||
if (AudioMixer::shouldReplicateTo(node, *downstreamNode)) {
|
||||
// construct the packet only once, if we have any downstream audio mixers to send to
|
||||
if (!packet) {
|
||||
// construct an NLPacket to send to the replicant that has the contents of the received packet
|
||||
|
@ -687,14 +688,14 @@ void AudioMixerClientData::setupCodecForReplicatedAgent(QSharedPointer<ReceivedM
|
|||
// pull the codec string from the packet
|
||||
auto codecString = message->readString();
|
||||
|
||||
qDebug() << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID())
|
||||
if (codecString != _selectedCodecName) {
|
||||
qDebug() << "Manually setting codec for replicated agent" << uuidStringWithoutCurlyBraces(getNodeID())
|
||||
<< "-" << codecString;
|
||||
|
||||
const std::pair<QString, CodecPluginPointer> codec = AudioMixer::negotiateCodec({ codecString });
|
||||
setupCodec(codec.second, codec.first);
|
||||
const std::pair<QString, CodecPluginPointer> codec = AudioMixer::negotiateCodec({ codecString });
|
||||
setupCodec(codec.second, codec.first);
|
||||
|
||||
_hasSetupCodecForUpstreamNode = true;
|
||||
|
||||
// seek back to the beginning of the message so other readers are in the right place
|
||||
message->seek(0);
|
||||
// seek back to the beginning of the message so other readers are in the right place
|
||||
message->seek(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,8 +184,6 @@ private:
|
|||
|
||||
bool _shouldMuteClient { false };
|
||||
bool _requestsDomainListData { false };
|
||||
|
||||
bool _hasSetupCodecForUpstreamNode { false };
|
||||
};
|
||||
|
||||
#endif // hifi_AudioMixerClientData_h
|
||||
|
|
|
@ -66,7 +66,6 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
|||
connect(nodeList.data(), &NodeList::nodeAdded, this, [this](const SharedNodePointer& node) {
|
||||
if (node->getType() == NodeType::DownstreamAvatarMixer) {
|
||||
getOrCreateClientData(node);
|
||||
node->activatePublicSocket();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -144,8 +143,8 @@ void AvatarMixer::optionallyReplicatePacket(ReceivedMessage& message, const Node
|
|||
std::unique_ptr<NLPacket> packet;
|
||||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->eachMatchingNode([&](const SharedNodePointer& node) {
|
||||
return node->getType() == NodeType::DownstreamAvatarMixer;
|
||||
nodeList->eachMatchingNode([&](const SharedNodePointer& downstreamNode) {
|
||||
return shouldReplicateTo(node, *downstreamNode);
|
||||
}, [&](const SharedNodePointer& node) {
|
||||
if (!packet) {
|
||||
// construct an NLPacket to send to the replicant that has the contents of the received packet
|
||||
|
@ -165,10 +164,6 @@ void AvatarMixer::queueIncomingPacket(QSharedPointer<ReceivedMessage> message, S
|
|||
_queueIncomingPacketElapsedTime += (end - start);
|
||||
}
|
||||
|
||||
|
||||
AvatarMixer::~AvatarMixer() {
|
||||
}
|
||||
|
||||
void AvatarMixer::sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) {
|
||||
if (destinationNode->getType() == NodeType::Agent && !destinationNode->isUpstream()) {
|
||||
QByteArray individualData = nodeData->getAvatar().identityByteArray();
|
||||
|
@ -331,7 +326,13 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) {
|
|||
}
|
||||
}
|
||||
if (sendIdentity) {
|
||||
sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name or avatar.
|
||||
|
||||
// since this packet includes a change to either the skeleton model URL or the display name
|
||||
// it needs a new sequence number
|
||||
nodeData->getAvatar().pushIdentitySequenceNumber();
|
||||
|
||||
// tell node whose name changed about its new session display name or avatar.
|
||||
sendIdentityPacket(nodeData, node);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -423,8 +424,8 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
|
|||
nodeList->eachMatchingNode([&](const SharedNodePointer& node) {
|
||||
// we relay avatar kill packets to agents that are not upstream
|
||||
// and downstream avatar mixers, if the node that was just killed was being replicated
|
||||
return (node->getType() == NodeType::Agent && !node->isUpstream())
|
||||
|| (killedNode->isReplicated() && node->getType() == NodeType::DownstreamAvatarMixer);
|
||||
return (node->getType() == NodeType::Agent && !node->isUpstream()) ||
|
||||
(killedNode->isReplicated() && shouldReplicateTo(*killedNode, *node));
|
||||
}, [&](const SharedNodePointer& node) {
|
||||
if (node->getType() == NodeType::Agent) {
|
||||
if (!killPacket) {
|
||||
|
@ -856,7 +857,10 @@ AvatarMixerClientData* AvatarMixer::getOrCreateClientData(SharedNodePointer node
|
|||
|
||||
void AvatarMixer::domainSettingsRequestComplete() {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::DownstreamAvatarMixer, NodeType::EntityScriptServer });
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet({
|
||||
NodeType::Agent, NodeType::EntityScriptServer,
|
||||
NodeType::UpstreamAvatarMixer, NodeType::DownstreamAvatarMixer
|
||||
});
|
||||
|
||||
// parse the settings to pull out the values we need
|
||||
parseDomainServerSettings(nodeList->getDomainHandler().getSettingsObject());
|
||||
|
|
|
@ -28,7 +28,13 @@ class AvatarMixer : public ThreadedAssignment {
|
|||
Q_OBJECT
|
||||
public:
|
||||
AvatarMixer(ReceivedMessage& message);
|
||||
~AvatarMixer();
|
||||
|
||||
static bool shouldReplicateTo(const Node& from, const Node& to) {
|
||||
return to.getType() == NodeType::DownstreamAvatarMixer &&
|
||||
to.getPublicSocket() != from.getPublicSocket() &&
|
||||
to.getLocalSocket() != from.getLocalSocket();
|
||||
}
|
||||
|
||||
public slots:
|
||||
/// runs the avatar mixer
|
||||
void run() override;
|
||||
|
|
|
@ -79,13 +79,13 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData,
|
|||
}
|
||||
}
|
||||
|
||||
int AvatarMixerSlave::sendReplicatedIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) {
|
||||
if (destinationNode->getType() == NodeType::DownstreamAvatarMixer) {
|
||||
int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode) {
|
||||
if (AvatarMixer::shouldReplicateTo(agentNode, destinationNode)) {
|
||||
QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray();
|
||||
individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious
|
||||
auto identityPacket = NLPacket::create(PacketType::ReplicatedAvatarIdentity);
|
||||
auto identityPacket = NLPacketList::create(PacketType::ReplicatedAvatarIdentity, QByteArray(), true, true);
|
||||
identityPacket->write(individualData);
|
||||
DependencyManager::get<NodeList>()->sendUnreliablePacket(*identityPacket, *destinationNode);
|
||||
DependencyManager::get<NodeList>()->sendPacketList(std::move(identityPacket), destinationNode);
|
||||
_stats.numIdentityPackets++;
|
||||
return individualData.size();
|
||||
} else {
|
||||
|
@ -453,6 +453,10 @@ void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePoin
|
|||
nodeData->resetNumAvatarsSentLastFrame();
|
||||
|
||||
std::for_each(_begin, _end, [&](const SharedNodePointer& agentNode) {
|
||||
if (!AvatarMixer::shouldReplicateTo(*agentNode, *node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// collect agents that we have avatar data for that we are supposed to replicate
|
||||
if (agentNode->getType() == NodeType::Agent && agentNode->getLinkedData() && agentNode->isReplicated()) {
|
||||
const AvatarMixerClientData* agentNodeData = reinterpret_cast<const AvatarMixerClientData*>(agentNode->getLinkedData());
|
||||
|
@ -479,7 +483,7 @@ void AvatarMixerSlave::broadcastAvatarDataToDownstreamMixer(const SharedNodePoin
|
|||
auto lastBroadcastTime = nodeData->getLastBroadcastTime(agentNode->getUUID());
|
||||
if (lastBroadcastTime <= agentNodeData->getIdentityChangeTimestamp()
|
||||
|| (start - lastBroadcastTime) >= REBROADCAST_IDENTITY_TO_DOWNSTREAM_EVERY_US) {
|
||||
sendReplicatedIdentityPacket(agentNodeData, node);
|
||||
sendReplicatedIdentityPacket(*agentNode, agentNodeData, *node);
|
||||
nodeData->setLastBroadcastTime(agentNode->getUUID(), start);
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ public:
|
|||
|
||||
private:
|
||||
int sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode);
|
||||
int sendReplicatedIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode);
|
||||
int sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode);
|
||||
|
||||
void broadcastAvatarDataToAgent(const SharedNodePointer& node);
|
||||
void broadcastAvatarDataToDownstreamMixer(const SharedNodePointer& node);
|
||||
|
|
|
@ -35,6 +35,23 @@ macro(SETUP_HIFI_LIBRARY)
|
|||
endif()
|
||||
endforeach()
|
||||
|
||||
# add compiler flags to AVX512 source files, if supported by compiler
|
||||
include(CheckCXXCompilerFlag)
|
||||
file(GLOB_RECURSE AVX512_SRCS "src/avx512/*.cpp" "src/avx512/*.c")
|
||||
foreach(SRC ${AVX512_SRCS})
|
||||
if (WIN32)
|
||||
check_cxx_compiler_flag("/arch:AVX512" COMPILER_SUPPORTS_AVX512)
|
||||
if (COMPILER_SUPPORTS_AVX512)
|
||||
set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS /arch:AVX512)
|
||||
endif()
|
||||
elseif (APPLE OR UNIX)
|
||||
check_cxx_compiler_flag("-mavx512f" COMPILER_SUPPORTS_AVX512)
|
||||
if (COMPILER_SUPPORTS_AVX512)
|
||||
set_source_files_properties(${SRC} PROPERTIES COMPILE_FLAGS -mavx512f)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
setup_memory_debugger()
|
||||
|
||||
# create a library and set the property so it can be referenced later
|
||||
|
|
|
@ -29,6 +29,63 @@
|
|||
!include "WinVer.nsh"
|
||||
|
||||
;--------------------------------
|
||||
; Utilities and Functions
|
||||
;--------------------------------
|
||||
; START String Contains Macro
|
||||
; Taken from http://nsis.sourceforge.net/StrContains
|
||||
;--------------------------------
|
||||
; StrContains
|
||||
; This function does a case sensitive searches for an occurrence of a substring in a string.
|
||||
; It returns the substring if it is found.
|
||||
; Otherwise it returns null("").
|
||||
; Written by kenglish_hi
|
||||
; Adapted from StrReplace written by dandaman32
|
||||
|
||||
|
||||
Var STR_HAYSTACK
|
||||
Var STR_NEEDLE
|
||||
Var STR_CONTAINS_VAR_1
|
||||
Var STR_CONTAINS_VAR_2
|
||||
Var STR_CONTAINS_VAR_3
|
||||
Var STR_CONTAINS_VAR_4
|
||||
Var STR_RETURN_VAR
|
||||
|
||||
Function StrContains
|
||||
Exch $STR_NEEDLE
|
||||
Exch 1
|
||||
Exch $STR_HAYSTACK
|
||||
; Uncomment to debug
|
||||
;MessageBox MB_OK 'STR_NEEDLE = $STR_NEEDLE STR_HAYSTACK = $STR_HAYSTACK '
|
||||
StrCpy $STR_RETURN_VAR ""
|
||||
StrCpy $STR_CONTAINS_VAR_1 -1
|
||||
StrLen $STR_CONTAINS_VAR_2 $STR_NEEDLE
|
||||
StrLen $STR_CONTAINS_VAR_4 $STR_HAYSTACK
|
||||
loop:
|
||||
IntOp $STR_CONTAINS_VAR_1 $STR_CONTAINS_VAR_1 + 1
|
||||
StrCpy $STR_CONTAINS_VAR_3 $STR_HAYSTACK $STR_CONTAINS_VAR_2 $STR_CONTAINS_VAR_1
|
||||
StrCmp $STR_CONTAINS_VAR_3 $STR_NEEDLE found
|
||||
StrCmp $STR_CONTAINS_VAR_1 $STR_CONTAINS_VAR_4 done
|
||||
Goto loop
|
||||
found:
|
||||
StrCpy $STR_RETURN_VAR $STR_NEEDLE
|
||||
Goto done
|
||||
done:
|
||||
Pop $STR_NEEDLE ;Prevent "invalid opcode" errors and keep the
|
||||
Exch $STR_RETURN_VAR
|
||||
FunctionEnd
|
||||
|
||||
!macro _StrContainsConstructor OUT NEEDLE HAYSTACK
|
||||
Push `${HAYSTACK}`
|
||||
Push `${NEEDLE}`
|
||||
Call StrContains
|
||||
Pop `${OUT}`
|
||||
!macroend
|
||||
|
||||
!define StrContains '!insertmacro "_StrContainsConstructor"'
|
||||
;--------------------------------
|
||||
; END String Contains Macro
|
||||
;--------------------------------
|
||||
;--------------------------------
|
||||
;General
|
||||
; leverage the UAC NSIS plugin to promote uninstaller to elevated privileges
|
||||
!include UAC.nsh
|
||||
|
@ -92,6 +149,7 @@
|
|||
; inter-component dependencies.
|
||||
Var AR_SecFlags
|
||||
Var AR_RegFlags
|
||||
Var substringResult
|
||||
@CPACK_NSIS_SECTION_SELECTED_VARS@
|
||||
|
||||
; Loads the "selected" flag for the section named SecName into the
|
||||
|
@ -119,9 +177,11 @@ Var AR_RegFlags
|
|||
ClearErrors
|
||||
;Reading component status from registry
|
||||
ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" "Installed"
|
||||
IfErrors "default_${SecName}"
|
||||
IfErrors "checkBeforeDefault_${SecName}"
|
||||
;Status will stay default if registry value not found
|
||||
;(component was never installed)
|
||||
|
||||
"set_initSection_${SecName}:"
|
||||
IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits
|
||||
SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags
|
||||
IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off
|
||||
|
@ -137,6 +197,18 @@ Var AR_RegFlags
|
|||
"default_${SecName}:"
|
||||
|
||||
!insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected
|
||||
Goto "end_initSection_${SecName}"
|
||||
|
||||
"checkBeforeDefault_${SecName}:"
|
||||
${StrContains} $substringResult "/nSandboxIfNew" $CMDLINE
|
||||
${If} "${SecName}" == "server"
|
||||
${AndIfNot} $substringResult == ""
|
||||
StrCpy $AR_RegFlags 0
|
||||
Goto "set_initSection_${SecName}"
|
||||
${Else}
|
||||
Goto "default_${SecName}"
|
||||
${EndIf}
|
||||
"end_initSection_${SecName}:"
|
||||
!macroend
|
||||
|
||||
!macro FinishSection SecName
|
||||
|
@ -432,6 +504,10 @@ Function PostInstallOptionsPage
|
|||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetPostInstallOption $LaunchServerNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||
${StrContains} $substringResult "/forceNoLaunchServer" $CMDLINE
|
||||
${IfNot} $substringResult == ""
|
||||
${NSD_SetState} $LaunchServerNowCheckbox ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
|
||||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
${EndIf}
|
||||
|
@ -442,6 +518,10 @@ Function PostInstallOptionsPage
|
|||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetPostInstallOption $LaunchClientNowCheckbox @CLIENT_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||
${StrContains} $substringResult "/forceNoLaunchClient" $CMDLINE
|
||||
${IfNot} $substringResult == ""
|
||||
${NSD_SetState} $LaunchClientNowCheckbox ${BST_UNCHECKED}
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
${If} @PR_BUILD@ == 1
|
||||
|
|
|
@ -1339,6 +1339,7 @@
|
|||
{
|
||||
"name": "broadcasting",
|
||||
"label": "Broadcasting",
|
||||
"restart": false,
|
||||
"settings": [
|
||||
{
|
||||
"name": "users",
|
||||
|
@ -1395,6 +1396,46 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "upstream_servers",
|
||||
"label": "Broadcasting Servers",
|
||||
"assignment-types": [0,1],
|
||||
"type": "table",
|
||||
"advanced": true,
|
||||
"can_add_new_rows": true,
|
||||
"help": "Servers that broadcast data to this domain",
|
||||
"numbered": false,
|
||||
"columns": [
|
||||
{
|
||||
"name": "address",
|
||||
"label": "Address",
|
||||
"can_set": true
|
||||
},
|
||||
{
|
||||
"name": "port",
|
||||
"label": "Port",
|
||||
"can_set": true
|
||||
},
|
||||
{
|
||||
"name": "server_type",
|
||||
"label": "Server Type",
|
||||
"type": "select",
|
||||
"placeholder": "Audio Mixer",
|
||||
"default": "Audio Mixer",
|
||||
"can_set": true,
|
||||
"options": [
|
||||
{
|
||||
"value": "Audio Mixer",
|
||||
"label": "Audio Mixer"
|
||||
},
|
||||
{
|
||||
"value": "Avatar Mixer",
|
||||
"label": "Avatar Mixer"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<link href="/css/sweetalert.css" rel="stylesheet" media="screen">
|
||||
<link href="/css/bootstrap-switch.min.css" rel="stylesheet" media="screen">
|
||||
|
||||
<script src='/js/sweetalert.min.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
|
@ -37,6 +38,8 @@
|
|||
</li>
|
||||
<li><a href="/content/">Content</a></li>
|
||||
<li><a href="/settings/">Settings</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-right navbar-nav">
|
||||
<li><a href="#" id="restart-server"><span class="glyphicon glyphicon-refresh"></span> Restart</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -33,9 +33,17 @@ $(document).ready(function(){
|
|||
return this.href == url;
|
||||
}).parent().addClass('active');
|
||||
|
||||
$('body').on('click', '#restart-server', function(e){
|
||||
$.get("/restart");
|
||||
showRestartModal();
|
||||
$('body').on('click', '#restart-server', function(e) {
|
||||
swal( {
|
||||
title: "Are you sure?",
|
||||
text: "This will restart your domain server, causing your domain to be briefly offline.",
|
||||
type: "warning",
|
||||
html: true,
|
||||
showCancelButton: true
|
||||
}, function() {
|
||||
$.get("/restart");
|
||||
showRestartModal();
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
|
@ -86,7 +86,6 @@
|
|||
<script src='/js/underscore-keypath.min.js'></script>
|
||||
<script src='/js/bootbox.min.js'></script>
|
||||
<script src='js/bootstrap-switch.min.js'></script>
|
||||
<script src='/js/sweetalert.min.js'></script>
|
||||
<script src='js/settings.js'></script>
|
||||
<script src='js/form2js.min.js'></script>
|
||||
<script src='js/sha256.js'></script>
|
||||
|
|
|
@ -424,7 +424,11 @@ function postSettings(jsonSettings) {
|
|||
type: 'POST'
|
||||
}).done(function(data){
|
||||
if (data.status == "success") {
|
||||
showRestartModal();
|
||||
if ($(".save-button").html() === SAVE_BUTTON_LABEL_RESTART) {
|
||||
showRestartModal();
|
||||
} else {
|
||||
location.reload(true);
|
||||
}
|
||||
} else {
|
||||
showErrorMessage("Error", SETTINGS_ERROR_MESSAGE)
|
||||
reloadSettings();
|
||||
|
|
|
@ -435,8 +435,29 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
return SharedNodePointer();
|
||||
}
|
||||
|
||||
QUuid hintNodeID;
|
||||
|
||||
// in case this is a node that's failing to connect
|
||||
// double check we don't have the same node whose sockets match exactly already in the list
|
||||
limitedNodeList->eachNodeBreakable([&](const SharedNodePointer& node){
|
||||
if (node->getPublicSocket() == nodeConnection.publicSockAddr && node->getLocalSocket() == nodeConnection.localSockAddr) {
|
||||
// we have a node that already has these exact sockets - this can occur if a node
|
||||
// is failing to connect to the domain
|
||||
|
||||
// we'll re-use the existing node ID
|
||||
// as long as the user hasn't changed their username (by logging in or logging out)
|
||||
auto existingNodeData = static_cast<DomainServerNodeData*>(node->getLinkedData());
|
||||
|
||||
if (existingNodeData->getUsername() == username) {
|
||||
hintNodeID = node->getUUID();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// add the connecting node (or re-use the matched one from eachNodeBreakable above)
|
||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection);
|
||||
SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID);
|
||||
|
||||
// set the edit rights for this user
|
||||
newNode->setPermissions(userPerms);
|
||||
|
@ -464,12 +485,11 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
return newNode;
|
||||
}
|
||||
|
||||
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection) {
|
||||
SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
QUuid nodeID) {
|
||||
HifiSockAddr discoveredSocket = nodeConnection.senderSockAddr;
|
||||
SharedNetworkPeer connectedPeer = _icePeers.value(nodeConnection.connectUUID);
|
||||
|
||||
QUuid nodeID;
|
||||
|
||||
if (connectedPeer) {
|
||||
// this user negotiated a connection with us via ICE, so re-use their ICE client ID
|
||||
nodeID = nodeConnection.connectUUID;
|
||||
|
@ -479,8 +499,10 @@ SharedNodePointer DomainGatekeeper::addVerifiedNodeFromConnectRequest(const Node
|
|||
discoveredSocket = *connectedPeer->getActiveSocket();
|
||||
}
|
||||
} else {
|
||||
// we got a connectUUID we didn't recognize, randomly generate a new one
|
||||
nodeID = QUuid::createUuid();
|
||||
// we got a connectUUID we didn't recognize, either use the hinted node ID or randomly generate a new one
|
||||
if (nodeID.isNull()) {
|
||||
nodeID = QUuid::createUuid();
|
||||
}
|
||||
}
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
|
|
@ -76,7 +76,8 @@ private:
|
|||
SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
const QString& username,
|
||||
const QByteArray& usernameSignature);
|
||||
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection);
|
||||
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
QUuid nodeID = QUuid());
|
||||
|
||||
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
|
|
|
@ -87,7 +87,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
qDebug() << "[VERSION] VERSION:" << BuildInfo::VERSION;
|
||||
qDebug() << "[VERSION] BUILD_BRANCH:" << BuildInfo::BUILD_BRANCH;
|
||||
qDebug() << "[VERSION] BUILD_GLOBAL_SERVICES:" << BuildInfo::BUILD_GLOBAL_SERVICES;
|
||||
qDebug() << "[VERSION] We will be using this default ICE server:" << ICE_SERVER_DEFAULT_HOSTNAME;
|
||||
qDebug() << "[VERSION] We will be using this name to find ICE servers:" << _iceServerAddr;
|
||||
|
||||
|
||||
// make sure we have a fresh AccountManager instance
|
||||
|
@ -121,6 +121,8 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
this, &DomainServer::updateReplicatedNodes);
|
||||
connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated,
|
||||
this, &DomainServer::updateDownstreamNodes);
|
||||
connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated,
|
||||
this, &DomainServer::updateUpstreamNodes);
|
||||
|
||||
setupGroupCacheRefresh();
|
||||
|
||||
|
@ -135,6 +137,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
updateReplicatedNodes();
|
||||
updateDownstreamNodes();
|
||||
updateUpstreamNodes();
|
||||
|
||||
if (_type != NonMetaverse) {
|
||||
// if we have a metaverse domain, we'll use an access token for API calls
|
||||
|
@ -1549,7 +1552,7 @@ void DomainServer::sendHeartbeatToIceServer() {
|
|||
|
||||
} else {
|
||||
qDebug() << "Not sending ice-server heartbeat since there is no selected ice-server.";
|
||||
qDebug() << "Waiting for" << ICE_SERVER_DEFAULT_HOSTNAME << "host lookup response";
|
||||
qDebug() << "Waiting for" << _iceServerAddr << "host lookup response";
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2229,53 +2232,84 @@ void DomainServer::refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer&
|
|||
|
||||
static const QString BROADCASTING_SETTINGS_KEY = "broadcasting";
|
||||
|
||||
void DomainServer::updateDownstreamNodes() {
|
||||
struct ReplicationServerInfo {
|
||||
NodeType_t nodeType;
|
||||
HifiSockAddr sockAddr;
|
||||
};
|
||||
|
||||
ReplicationServerInfo serverInformationFromSettings(QVariantMap serverMap, ReplicationServerDirection direction) {
|
||||
static const QString REPLICATION_SERVER_ADDRESS = "address";
|
||||
static const QString REPLICATION_SERVER_PORT = "port";
|
||||
static const QString REPLICATION_SERVER_TYPE = "server_type";
|
||||
|
||||
if (serverMap.contains(REPLICATION_SERVER_ADDRESS) && serverMap.contains(REPLICATION_SERVER_PORT)
|
||||
&& serverMap.contains(REPLICATION_SERVER_TYPE)) {
|
||||
|
||||
auto nodeType = NodeType::fromString(serverMap[REPLICATION_SERVER_TYPE].toString());
|
||||
|
||||
ReplicationServerInfo serverInfo;
|
||||
|
||||
if (direction == Upstream) {
|
||||
serverInfo.nodeType = NodeType::upstreamType(nodeType);
|
||||
} else if (direction == Downstream) {
|
||||
serverInfo.nodeType = NodeType::downstreamType(nodeType);
|
||||
}
|
||||
|
||||
// read the address and port and construct a HifiSockAddr from them
|
||||
serverInfo.sockAddr = {
|
||||
serverMap[REPLICATION_SERVER_ADDRESS].toString(),
|
||||
(quint16) serverMap[REPLICATION_SERVER_PORT].toString().toInt()
|
||||
};
|
||||
|
||||
return serverInfo;
|
||||
}
|
||||
|
||||
return { NodeType::Unassigned, HifiSockAddr() };
|
||||
}
|
||||
|
||||
void DomainServer::updateReplicationNodes(ReplicationServerDirection direction) {
|
||||
auto settings = _settingsManager.getSettingsMap();
|
||||
|
||||
if (settings.contains(BROADCASTING_SETTINGS_KEY)) {
|
||||
auto nodeList = DependencyManager::get<LimitedNodeList>();
|
||||
std::vector<HifiSockAddr> downstreamNodesInSettings;
|
||||
auto replicationSettings = settings.value(BROADCASTING_SETTINGS_KEY).toMap();
|
||||
if (replicationSettings.contains("downstream_servers")) {
|
||||
auto serversSettings = replicationSettings.value("downstream_servers").toList();
|
||||
std::vector<HifiSockAddr> replicationNodesInSettings;
|
||||
|
||||
std::vector<HifiSockAddr> knownDownstreamNodes;
|
||||
auto replicationSettings = settings.value(BROADCASTING_SETTINGS_KEY).toMap();
|
||||
|
||||
QString serversKey = direction == Upstream ? "upstream_servers" : "downstream_servers";
|
||||
QString replicationDirection = direction == Upstream ? "upstream" : "downstream";
|
||||
|
||||
if (replicationSettings.contains(serversKey)) {
|
||||
auto serversSettings = replicationSettings.value(serversKey).toList();
|
||||
|
||||
std::vector<HifiSockAddr> knownReplicationNodes;
|
||||
nodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||
if (NodeType::isDownstream(otherNode->getType())) {
|
||||
knownDownstreamNodes.push_back(otherNode->getPublicSocket());
|
||||
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|
||||
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
|
||||
knownReplicationNodes.push_back(otherNode->getPublicSocket());
|
||||
}
|
||||
});
|
||||
|
||||
for (auto& server : serversSettings) {
|
||||
auto downstreamServer = server.toMap();
|
||||
auto replicationServer = serverInformationFromSettings(server.toMap(), direction);
|
||||
|
||||
static const QString DOWNSTREAM_SERVER_ADDRESS = "address";
|
||||
static const QString DOWNSTREAM_SERVER_PORT = "port";
|
||||
static const QString DOWNSTREAM_SERVER_TYPE = "server_type";
|
||||
if (!replicationServer.sockAddr.isNull() && replicationServer.nodeType != NodeType::Unassigned) {
|
||||
// make sure we have the settings we need for this replication server
|
||||
replicationNodesInSettings.push_back(replicationServer.sockAddr);
|
||||
|
||||
// make sure we have the settings we need for this downstream server
|
||||
if (downstreamServer.contains(DOWNSTREAM_SERVER_ADDRESS) && downstreamServer.contains(DOWNSTREAM_SERVER_PORT)) {
|
||||
|
||||
auto nodeType = NodeType::fromString(downstreamServer[DOWNSTREAM_SERVER_TYPE].toString());
|
||||
auto downstreamNodeType = NodeType::downstreamType(nodeType);
|
||||
|
||||
// read the address and port and construct a HifiSockAddr from them
|
||||
HifiSockAddr downstreamServerAddr {
|
||||
downstreamServer[DOWNSTREAM_SERVER_ADDRESS].toString(),
|
||||
(quint16) downstreamServer[DOWNSTREAM_SERVER_PORT].toString().toInt()
|
||||
};
|
||||
downstreamNodesInSettings.push_back(downstreamServerAddr);
|
||||
|
||||
bool knownNode = find(knownDownstreamNodes.cbegin(), knownDownstreamNodes.cend(),
|
||||
downstreamServerAddr) != knownDownstreamNodes.cend();
|
||||
bool knownNode = find(knownReplicationNodes.cbegin(), knownReplicationNodes.cend(),
|
||||
replicationServer.sockAddr) != knownReplicationNodes.cend();
|
||||
if (!knownNode) {
|
||||
// manually add the downstream node to our node list
|
||||
auto node = nodeList->addOrUpdateNode(QUuid::createUuid(), downstreamNodeType,
|
||||
downstreamServerAddr, downstreamServerAddr);
|
||||
// manually add the replication node to our node list
|
||||
auto node = nodeList->addOrUpdateNode(QUuid::createUuid(), replicationServer.nodeType,
|
||||
replicationServer.sockAddr, replicationServer.sockAddr,
|
||||
false, direction == Upstream);
|
||||
node->setIsForcedNeverSilent(true);
|
||||
|
||||
qDebug() << "Adding downstream node:" << node->getUUID() << downstreamServerAddr;
|
||||
qDebug() << "Adding" << (direction == Upstream ? "upstream" : "downstream")
|
||||
<< "node:" << node->getUUID() << replicationServer.sockAddr;
|
||||
|
||||
// manually activate the public socket for the downstream node
|
||||
// manually activate the public socket for the replication node
|
||||
node->activatePublicSocket();
|
||||
}
|
||||
}
|
||||
|
@ -2288,11 +2322,13 @@ void DomainServer::updateDownstreamNodes() {
|
|||
// we cannot recursively take the write lock required by handleKillNode)
|
||||
std::vector<SharedNodePointer> nodesToKill;
|
||||
nodeList->eachNode([&](const SharedNodePointer& otherNode) {
|
||||
if (NodeType::isDownstream(otherNode->getType())) {
|
||||
bool nodeInSettings = find(downstreamNodesInSettings.cbegin(), downstreamNodesInSettings.cend(),
|
||||
otherNode->getPublicSocket()) != downstreamNodesInSettings.cend();
|
||||
if ((direction == Upstream && NodeType::isUpstream(otherNode->getType()))
|
||||
|| (direction == Downstream && NodeType::isDownstream(otherNode->getType()))) {
|
||||
bool nodeInSettings = find(replicationNodesInSettings.cbegin(), replicationNodesInSettings.cend(),
|
||||
otherNode->getPublicSocket()) != replicationNodesInSettings.cend();
|
||||
if (!nodeInSettings) {
|
||||
qDebug() << "Removing downstream node:" << otherNode->getUUID() << otherNode->getPublicSocket();
|
||||
qDebug() << "Removing" << replicationDirection
|
||||
<< "node:" << otherNode->getUUID() << otherNode->getPublicSocket();
|
||||
nodesToKill.push_back(otherNode);
|
||||
}
|
||||
}
|
||||
|
@ -2304,6 +2340,14 @@ void DomainServer::updateDownstreamNodes() {
|
|||
}
|
||||
}
|
||||
|
||||
void DomainServer::updateDownstreamNodes() {
|
||||
updateReplicationNodes(Downstream);
|
||||
}
|
||||
|
||||
void DomainServer::updateUpstreamNodes() {
|
||||
updateReplicationNodes(Upstream);
|
||||
}
|
||||
|
||||
void DomainServer::updateReplicatedNodes() {
|
||||
// Make sure we have downstream nodes in our list
|
||||
auto settings = _settingsManager.getSettingsMap();
|
||||
|
@ -2639,7 +2683,7 @@ void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) {
|
|||
_iceAddressLookupID = -1;
|
||||
|
||||
if (hostInfo.error() != QHostInfo::NoError) {
|
||||
qWarning() << "IP address lookup failed for" << ICE_SERVER_DEFAULT_HOSTNAME << ":" << hostInfo.errorString();
|
||||
qWarning() << "IP address lookup failed for" << _iceServerAddr << ":" << hostInfo.errorString();
|
||||
|
||||
// if we don't have an ICE server to use yet, trigger a retry
|
||||
if (_iceServerSocket.isNull()) {
|
||||
|
@ -2654,7 +2698,7 @@ void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) {
|
|||
_iceServerAddresses = hostInfo.addresses();
|
||||
|
||||
if (countBefore == 0) {
|
||||
qInfo() << "Found" << _iceServerAddresses.count() << "ice-server IP addresses for" << ICE_SERVER_DEFAULT_HOSTNAME;
|
||||
qInfo() << "Found" << _iceServerAddresses.count() << "ice-server IP addresses for" << _iceServerAddr;
|
||||
}
|
||||
|
||||
if (_iceServerSocket.isNull()) {
|
||||
|
@ -2689,7 +2733,7 @@ void DomainServer::randomizeICEServerAddress(bool shouldTriggerHostLookup) {
|
|||
// so clear the set of failed addresses and start going through them again
|
||||
|
||||
qWarning() << "All current ice-server addresses have failed - re-attempting all current addresses for"
|
||||
<< ICE_SERVER_DEFAULT_HOSTNAME;
|
||||
<< _iceServerAddr;
|
||||
|
||||
_failedIceServerAddresses.clear();
|
||||
candidateICEAddresses = _iceServerAddresses;
|
||||
|
|
|
@ -39,6 +39,11 @@ typedef QMultiHash<QUuid, WalletTransaction*> TransactionHash;
|
|||
using Subnet = QPair<QHostAddress, int>;
|
||||
using SubnetList = std::vector<Subnet>;
|
||||
|
||||
enum ReplicationServerDirection {
|
||||
Upstream,
|
||||
Downstream
|
||||
};
|
||||
|
||||
class DomainServer : public QCoreApplication, public HTTPSRequestHandler {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -104,6 +109,7 @@ private slots:
|
|||
|
||||
void updateReplicatedNodes();
|
||||
void updateDownstreamNodes();
|
||||
void updateUpstreamNodes();
|
||||
|
||||
signals:
|
||||
void iceServerChanged();
|
||||
|
@ -170,6 +176,8 @@ private:
|
|||
|
||||
QString pathForRedirect(QString path = QString()) const;
|
||||
|
||||
void updateReplicationNodes(ReplicationServerDirection direction);
|
||||
|
||||
SubnetList _acSubnetWhitelist;
|
||||
|
||||
std::vector<QString> _replicatedUsernames;
|
||||
|
|
|
@ -39,10 +39,26 @@ ScrollingWindow {
|
|||
property alias x: root.x
|
||||
property alias y: root.y
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
interval: 100
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: updateRunningScripts();
|
||||
}
|
||||
|
||||
Component {
|
||||
id: listModelBuilder
|
||||
ListModel { }
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ScriptDiscoveryService
|
||||
onScriptCountChanged: updateRunningScripts();
|
||||
onScriptCountChanged: {
|
||||
runningScriptsModel = listModelBuilder.createObject(root);
|
||||
refreshTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
|
@ -65,10 +81,16 @@ ScrollingWindow {
|
|||
b = simplify(b.path);
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
});
|
||||
runningScriptsModel.clear()
|
||||
// Calling `runningScriptsModel.clear()` here instead of creating a new object
|
||||
// triggers some kind of weird heap corruption deep inside Qt. So instead of
|
||||
// modifying the model in place, possibly triggering behaviors in the table
|
||||
// instead we create a new `ListModel`, populate it and update the
|
||||
// existing model atomically.
|
||||
var newRunningScriptsModel = listModelBuilder.createObject(root);
|
||||
for (var i = 0; i < runningScripts.length; ++i) {
|
||||
runningScriptsModel.append(runningScripts[i]);
|
||||
newRunningScriptsModel.append(runningScripts[i]);
|
||||
}
|
||||
runningScriptsModel = newRunningScriptsModel;
|
||||
}
|
||||
|
||||
function loadScript(script) {
|
||||
|
|
|
@ -99,8 +99,8 @@
|
|||
#include <OctalCode.h>
|
||||
#include <OctreeSceneStats.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <gl/OffscreenQmlSurfaceCache.h>
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <ui/OffscreenQmlSurfaceCache.h>
|
||||
#include <PathUtils.h>
|
||||
#include <PerfStat.h>
|
||||
#include <PhysicsEngine.h>
|
||||
|
@ -122,7 +122,8 @@
|
|||
#include <ScriptEngines.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <SoundCache.h>
|
||||
#include <TabletScriptingInterface.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
#include <ui/ToolbarScriptingInterface.h>
|
||||
#include <Tooltip.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <UserActivityLogger.h>
|
||||
|
@ -163,7 +164,6 @@
|
|||
#include "scripting/SettingsScriptingInterface.h"
|
||||
#include "scripting/WindowScriptingInterface.h"
|
||||
#include "scripting/ControllerScriptingInterface.h"
|
||||
#include "scripting/ToolbarScriptingInterface.h"
|
||||
#include "scripting/RatesScriptingInterface.h"
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
|
||||
#include "SpeechRecognizer.h"
|
||||
|
@ -438,6 +438,11 @@ static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement";
|
|||
static const QString STATE_GROUNDED = "Grounded";
|
||||
static const QString STATE_NAV_FOCUSED = "NavigationFocused";
|
||||
|
||||
// Statically provided display and input plugins
|
||||
extern DisplayPluginList getDisplayPlugins();
|
||||
extern InputPluginList getInputPlugins();
|
||||
extern void saveInputPluginSettings(const InputPluginList& plugins);
|
||||
|
||||
bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
||||
const char** constArgv = const_cast<const char**>(argv);
|
||||
|
||||
|
@ -477,6 +482,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
|
||||
Setting::init();
|
||||
|
||||
// Tell the plugin manager about our statically linked plugins
|
||||
PluginManager::setInputPluginProvider([] { return getInputPlugins(); });
|
||||
PluginManager::setDisplayPluginProvider([] { return getDisplayPlugins(); });
|
||||
PluginManager::setInputPluginSettingsPersister([](const InputPluginList& plugins) { saveInputPluginSettings(plugins); });
|
||||
|
||||
if (auto steamClient = PluginManager::getInstance()->getSteamClientPlugin()) {
|
||||
steamClient->init();
|
||||
}
|
||||
|
@ -900,6 +910,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_saveAvatarOverrideUrl = true;
|
||||
}
|
||||
|
||||
QString defaultScriptsLocation = getCmdOption(argc, constArgv, "--scripts");
|
||||
if (!defaultScriptsLocation.isEmpty()) {
|
||||
PathUtils::defaultScriptsLocation(defaultScriptsLocation);
|
||||
}
|
||||
|
||||
_glWidget = new GLCanvas();
|
||||
getApplicationCompositor().setRenderingWidget(_glWidget);
|
||||
_window->setCentralWidget(_glWidget);
|
||||
|
@ -1164,7 +1179,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// force the model the look at the correct directory (weird order of operations issue)
|
||||
scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation());
|
||||
// do this as late as possible so that all required subsystems are initialized
|
||||
scriptEngines->loadScripts();
|
||||
// If we've overridden the default scripts location, just load default scripts
|
||||
// otherwise, load 'em all
|
||||
if (!defaultScriptsLocation.isEmpty()) {
|
||||
scriptEngines->loadDefaultScripts();
|
||||
scriptEngines->defaultScriptsLocationOverridden(true);
|
||||
} else {
|
||||
scriptEngines->loadScripts();
|
||||
}
|
||||
|
||||
// Make sure we don't time out during slow operations at startup
|
||||
updateHeartbeat();
|
||||
|
||||
|
@ -1789,6 +1812,8 @@ void Application::cleanupBeforeQuit() {
|
|||
#endif
|
||||
|
||||
// stop QML
|
||||
DependencyManager::destroy<TabletScriptingInterface>();
|
||||
DependencyManager::destroy<ToolbarScriptingInterface>();
|
||||
DependencyManager::destroy<OffscreenUi>();
|
||||
|
||||
DependencyManager::destroy<OffscreenQmlSurfaceCache>();
|
||||
|
@ -5530,8 +5555,16 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
|
||||
scriptEngine->registerGlobalObject("OffscreenFlags", DependencyManager::get<OffscreenUi>()->getFlags());
|
||||
scriptEngine->registerGlobalObject("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());
|
||||
|
||||
qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue<ToolbarProxy>, wrapperFromScriptValue<ToolbarProxy>);
|
||||
qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue<ToolbarButtonProxy>, wrapperFromScriptValue<ToolbarButtonProxy>);
|
||||
scriptEngine->registerGlobalObject("Toolbars", DependencyManager::get<ToolbarScriptingInterface>().data());
|
||||
|
||||
qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue<TabletProxy>, wrapperFromScriptValue<TabletProxy>);
|
||||
qScriptRegisterMetaType(scriptEngine, wrapperToScriptValue<TabletButtonProxy>, wrapperFromScriptValue<TabletButtonProxy>);
|
||||
scriptEngine->registerGlobalObject("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
|
||||
|
||||
DependencyManager::get<TabletScriptingInterface>().data()->setToolbarScriptingInterface(DependencyManager::get<ToolbarScriptingInterface>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("Window", DependencyManager::get<WindowScriptingInterface>().data());
|
||||
|
@ -5605,7 +5638,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
scriptEngine->registerGlobalObject("EntityScriptServerLog", entityScriptServerLog.data());
|
||||
scriptEngine->registerGlobalObject("AvatarInputs", AvatarInputs::getInstance());
|
||||
|
||||
|
||||
qScriptRegisterMetaType(scriptEngine, OverlayIDtoScriptValue, OverlayIDfromScriptValue);
|
||||
|
||||
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
|
||||
|
@ -5614,6 +5646,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
|||
connect(scriptEngine, &ScriptEngine::warningMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onWarningMessage);
|
||||
connect(scriptEngine, &ScriptEngine::infoMessage, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onInfoMessage);
|
||||
connect(scriptEngine, &ScriptEngine::clearDebugWindow, DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onClearDebugWindow);
|
||||
|
||||
}
|
||||
|
||||
bool Application::canAcceptURL(const QString& urlString) const {
|
||||
|
@ -5850,7 +5883,7 @@ void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const
|
|||
|
||||
void Application::showScriptLogs() {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/debugging/debugWindow.js");
|
||||
scriptEngines->loadScript(defaultScriptsLoc.toString());
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include <AudioClient.h>
|
||||
#include <CrashHelpers.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <TabletScriptingInterface.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
#include <display-plugins/DisplayPlugin.h>
|
||||
#include <PathUtils.h>
|
||||
#include <SettingHandle.h>
|
||||
|
@ -121,9 +121,14 @@ Menu::Menu() {
|
|||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
|
||||
// Edit > Reload All Scripts... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R,
|
||||
scriptEngines.data(), SLOT(reloadAllScripts()),
|
||||
action = addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R,
|
||||
nullptr, nullptr,
|
||||
QAction::NoRole, UNSPECIFIED_POSITION, "Advanced");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
DependencyManager::get<ScriptEngines>()->reloadAllScripts();
|
||||
DependencyManager::get<OffscreenUi>()->clearCache();
|
||||
});
|
||||
|
||||
|
||||
// Edit > Console... [advanced]
|
||||
addActionToQMenuAndActionHash(editMenu, MenuOption::Console, Qt::CTRL | Qt::ALT | Qt::Key_J,
|
||||
|
@ -192,6 +197,9 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableAvatarCollisions, 0, true,
|
||||
avatar.get(), SLOT(updateMotionBehaviorFromMenu()));
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableFlying, 0, true,
|
||||
avatar.get(), SLOT(setFlyingEnabled(bool)));
|
||||
|
||||
// Avatar > AvatarBookmarks related menus -- Note: the AvatarBookmarks class adds its own submenus here.
|
||||
auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>();
|
||||
avatarBookmarks->setupMenus(this, avatarMenu);
|
||||
|
@ -300,7 +308,7 @@ Menu::Menu() {
|
|||
// Settings > Avatar...
|
||||
action = addActionToQMenuAndActionHash(settingsMenu, "Avatar...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
qApp->showDialog(QString("hifi/dialogs/AvatarPreferencesDialog.qml"),
|
||||
qApp->showDialog(QString("hifi/dialogs/AvatarPreferencesDialog.qml"),
|
||||
QString("../../hifi/tablet/TabletAvatarPreferences.qml"), "AvatarPreferencesDialog");
|
||||
});
|
||||
|
||||
|
@ -626,7 +634,7 @@ Menu::Menu() {
|
|||
action = addActionToQMenuAndActionHash(audioDebugMenu, "Stats...");
|
||||
connect(action, &QAction::triggered, [] {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/audio/stats.js");
|
||||
scriptEngines->loadScript(defaultScriptsLoc.toString());
|
||||
});
|
||||
|
|
|
@ -94,6 +94,7 @@ namespace MenuOption {
|
|||
const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene";
|
||||
const QString EchoLocalAudio = "Echo Local Audio";
|
||||
const QString EchoServerAudio = "Echo Server Audio";
|
||||
const QString EnableFlying = "Enable Flying";
|
||||
const QString EnableAvatarCollisions = "Enable Avatar Collisions";
|
||||
const QString EnableInverseKinematics = "Enable Inverse Kinematics";
|
||||
const QString EntityScriptServerLog = "Entity Script Server Log";
|
||||
|
|
|
@ -593,12 +593,12 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
auto entityTreeRenderer = qApp->getEntities();
|
||||
EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr;
|
||||
if (entityTree) {
|
||||
bool flyingAllowed = true;
|
||||
bool zoneAllowsFlying = true;
|
||||
bool collisionlessAllowed = true;
|
||||
entityTree->withWriteLock([&] {
|
||||
std::shared_ptr<ZoneEntityItem> zone = entityTreeRenderer->myAvatarZone();
|
||||
if (zone) {
|
||||
flyingAllowed = zone->getFlyingAllowed();
|
||||
zoneAllowsFlying = zone->getFlyingAllowed();
|
||||
collisionlessAllowed = zone->getGhostingAllowed();
|
||||
}
|
||||
auto now = usecTimestampNow();
|
||||
|
@ -629,7 +629,7 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
entityTree->recurseTreeWithOperator(&moveOperator);
|
||||
}
|
||||
});
|
||||
_characterController.setFlyingAllowed(flyingAllowed);
|
||||
_characterController.setFlyingAllowed(zoneAllowsFlying && _enableFlying);
|
||||
_characterController.setCollisionlessAllowed(collisionlessAllowed);
|
||||
}
|
||||
|
||||
|
@ -2500,6 +2500,30 @@ void MyAvatar::updateMotionBehaviorFromMenu() {
|
|||
setCollisionsEnabled(menu->isOptionChecked(MenuOption::EnableAvatarCollisions));
|
||||
}
|
||||
|
||||
void MyAvatar::setFlyingEnabled(bool enabled) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setFlyingEnabled", Q_ARG(bool, enabled));
|
||||
return;
|
||||
}
|
||||
|
||||
_enableFlying = enabled;
|
||||
}
|
||||
|
||||
bool MyAvatar::isFlying() {
|
||||
// Avatar is Flying, and is not Falling, or Taking off
|
||||
return _characterController.getState() == CharacterController::State::Hover;
|
||||
}
|
||||
|
||||
bool MyAvatar::isInAir() {
|
||||
// If Avatar is Hover, Falling, or Taking off, they are in Air.
|
||||
return _characterController.getState() != CharacterController::State::Ground;
|
||||
}
|
||||
|
||||
bool MyAvatar::getFlyingEnabled() {
|
||||
// May return true even if client is not allowed to fly in the zone.
|
||||
return _enableFlying;
|
||||
}
|
||||
|
||||
void MyAvatar::setCollisionsEnabled(bool enabled) {
|
||||
|
||||
if (QThread::currentThread() != thread()) {
|
||||
|
|
|
@ -507,6 +507,11 @@ public:
|
|||
|
||||
bool hasDriveInput() const;
|
||||
|
||||
Q_INVOKABLE bool isFlying();
|
||||
Q_INVOKABLE bool isInAir();
|
||||
Q_INVOKABLE void setFlyingEnabled(bool enabled);
|
||||
Q_INVOKABLE bool getFlyingEnabled();
|
||||
|
||||
Q_INVOKABLE void setCollisionsEnabled(bool enabled);
|
||||
Q_INVOKABLE bool getCollisionsEnabled();
|
||||
Q_INVOKABLE void setCharacterControllerEnabled(bool enabled); // deprecated
|
||||
|
@ -569,6 +574,7 @@ public slots:
|
|||
void setEnableDebugDrawIKTargets(bool isEnabled);
|
||||
void setEnableDebugDrawIKConstraints(bool isEnabled);
|
||||
void setEnableDebugDrawIKChains(bool isEnabled);
|
||||
|
||||
bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); }
|
||||
void setEnableMeshVisible(bool isEnabled);
|
||||
void setUseAnimPreAndPostRotations(bool isEnabled);
|
||||
|
@ -641,6 +647,7 @@ private:
|
|||
std::array<float, MAX_DRIVE_KEYS> _driveKeys;
|
||||
std::bitset<MAX_DRIVE_KEYS> _disabledDriveKeys;
|
||||
|
||||
bool _enableFlying { true };
|
||||
bool _wasPushing { false };
|
||||
bool _isPushing { false };
|
||||
bool _isBeingPushed { false };
|
||||
|
@ -790,7 +797,6 @@ private:
|
|||
ThreadSafeValueCache<controller::Pose> _rightArmControllerPoseInSensorFrameCache{ controller::Pose() };
|
||||
|
||||
bool _hmdLeanRecenterEnabled = true;
|
||||
|
||||
AnimPose _prePhysicsRoomPose;
|
||||
std::mutex _holdActionsMutex;
|
||||
std::vector<AvatarActionHold*> _holdActions;
|
||||
|
|
|
@ -9,18 +9,30 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "AudioDevices.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "AudioClient.h"
|
||||
#include "Audio.h"
|
||||
|
||||
#include "UserActivityLogger.h"
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
Setting::Handle<QString> inputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "INPUT" }};
|
||||
Setting::Handle<QString> outputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "OUTPUT" }};
|
||||
Setting::Handle<QString> inputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }};
|
||||
Setting::Handle<QString> outputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }};
|
||||
static Setting::Handle<QString> desktopInputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, "INPUT" }};
|
||||
static Setting::Handle<QString> desktopOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::DESKTOP, "OUTPUT" }};
|
||||
static Setting::Handle<QString> hmdInputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }};
|
||||
static Setting::Handle<QString> hmdOutputDeviceSetting { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }};
|
||||
|
||||
Setting::Handle<QString>& getSetting(bool contextIsHMD, QAudio::Mode mode) {
|
||||
if (mode == QAudio::AudioInput) {
|
||||
return contextIsHMD ? hmdInputDeviceSetting : desktopInputDeviceSetting;
|
||||
} else { // if (mode == QAudio::AudioOutput)
|
||||
return contextIsHMD ? hmdOutputDeviceSetting : desktopOutputDeviceSetting;
|
||||
}
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> AudioDeviceList::_roles {
|
||||
{ Qt::DisplayRole, "display" },
|
||||
|
@ -43,32 +55,37 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const {
|
|||
}
|
||||
|
||||
bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, int role) {
|
||||
if (!index.isValid() || index.row() >= _devices.size()) {
|
||||
if (!index.isValid() || index.row() >= _devices.size() || role != Qt::CheckStateRole) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// only allow switching to a new device, not deactivating an in-use device
|
||||
auto selected = value.toBool();
|
||||
if (!selected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return setDevice(index.row(), true);
|
||||
}
|
||||
|
||||
bool AudioDeviceList::setDevice(int row, bool fromUser) {
|
||||
bool success = false;
|
||||
auto& device = _devices[row];
|
||||
|
||||
if (role == Qt::CheckStateRole) {
|
||||
auto selected = value.toBool();
|
||||
auto& device = _devices[index.row()];
|
||||
// skip if already selected
|
||||
if (!device.selected) {
|
||||
auto client = DependencyManager::get<AudioClient>();
|
||||
QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, success),
|
||||
Q_ARG(QAudio::Mode, _mode),
|
||||
Q_ARG(const QAudioDeviceInfo&, device.info));
|
||||
|
||||
// only allow switching to a new device, not deactivating an in-use device
|
||||
if (selected
|
||||
// skip if already selected
|
||||
&& selected != device.selected) {
|
||||
|
||||
auto client = DependencyManager::get<AudioClient>();
|
||||
QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, success),
|
||||
Q_ARG(QAudio::Mode, _mode),
|
||||
Q_ARG(const QAudioDeviceInfo&, device.info));
|
||||
|
||||
if (success) {
|
||||
device.selected = true;
|
||||
emit deviceSelected(device.info);
|
||||
emit deviceChanged(device.info);
|
||||
if (success) {
|
||||
device.selected = true;
|
||||
if (fromUser) {
|
||||
emit deviceSelected(device.info, _selectedDevice);
|
||||
}
|
||||
emit deviceChanged(device.info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,12 +105,12 @@ void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) {
|
|||
}
|
||||
}
|
||||
if (i < rowCount()) {
|
||||
success = setData(createIndex(i, 0), true, Qt::CheckStateRole);
|
||||
success = setDevice(i, false);
|
||||
}
|
||||
|
||||
// the selection failed - reset it
|
||||
if (!success) {
|
||||
emit deviceSelected(QAudioDeviceInfo());
|
||||
emit deviceSelected();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,48 +184,55 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) {
|
|||
_inputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioInput));
|
||||
_outputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioOutput));
|
||||
|
||||
connect(&_inputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onInputDeviceSelected);
|
||||
connect(&_outputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onOutputDeviceSelected);
|
||||
connect(&_inputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) {
|
||||
onDeviceSelected(QAudio::AudioInput, device, previousDevice);
|
||||
});
|
||||
connect(&_outputs, &AudioDeviceList::deviceSelected, [&](const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) {
|
||||
onDeviceSelected(QAudio::AudioOutput, device, previousDevice);
|
||||
});
|
||||
}
|
||||
|
||||
void AudioDevices::onContextChanged(const QString& context) {
|
||||
QString input;
|
||||
QString output;
|
||||
if (_contextIsHMD) {
|
||||
input = inputDeviceHMD.get();
|
||||
output = outputDeviceHMD.get();
|
||||
} else {
|
||||
input = inputDeviceDesktop.get();
|
||||
output = outputDeviceDesktop.get();
|
||||
}
|
||||
auto input = getSetting(_contextIsHMD, QAudio::AudioInput).get();
|
||||
auto output = getSetting(_contextIsHMD, QAudio::AudioOutput).get();
|
||||
|
||||
_inputs.resetDevice(_contextIsHMD, input);
|
||||
_outputs.resetDevice(_contextIsHMD, output);
|
||||
}
|
||||
|
||||
void AudioDevices::onInputDeviceSelected(const QAudioDeviceInfo& device) {
|
||||
QString deviceName;
|
||||
void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) {
|
||||
QString deviceName = device.isNull() ? QString() : device.deviceName();
|
||||
|
||||
auto& setting = getSetting(_contextIsHMD, mode);
|
||||
|
||||
// check for a previous device
|
||||
auto wasDefault = setting.get().isNull();
|
||||
|
||||
// store the selected device
|
||||
setting.set(deviceName);
|
||||
|
||||
// log the selected device
|
||||
if (!device.isNull()) {
|
||||
deviceName = device.deviceName();
|
||||
}
|
||||
QJsonObject data;
|
||||
|
||||
if (_contextIsHMD) {
|
||||
inputDeviceHMD.set(deviceName);
|
||||
} else {
|
||||
inputDeviceDesktop.set(deviceName);
|
||||
}
|
||||
}
|
||||
const QString MODE = "audio_mode";
|
||||
const QString INPUT = "INPUT";
|
||||
const QString OUTPUT = "OUTPUT"; data[MODE] = mode == QAudio::AudioInput ? INPUT : OUTPUT;
|
||||
|
||||
void AudioDevices::onOutputDeviceSelected(const QAudioDeviceInfo& device) {
|
||||
QString deviceName;
|
||||
if (!device.isNull()) {
|
||||
deviceName = device.deviceName();
|
||||
}
|
||||
const QString CONTEXT = "display_mode";
|
||||
data[CONTEXT] = _contextIsHMD ? Audio::HMD : Audio::DESKTOP;
|
||||
|
||||
if (_contextIsHMD) {
|
||||
outputDeviceHMD.set(deviceName);
|
||||
} else {
|
||||
outputDeviceDesktop.set(deviceName);
|
||||
const QString DISPLAY = "display_device";
|
||||
data[DISPLAY] = qApp->getActiveDisplayPlugin()->getName();
|
||||
|
||||
const QString DEVICE = "device";
|
||||
const QString PREVIOUS_DEVICE = "previous_device";
|
||||
const QString WAS_DEFAULT = "was_default";
|
||||
data[DEVICE] = deviceName;
|
||||
data[PREVIOUS_DEVICE] = previousDevice.deviceName();
|
||||
data[WAS_DEFAULT] = wasDefault;
|
||||
|
||||
UserActivityLogger::getInstance().logAction("selected_audio_device", data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,4 +263,4 @@ void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceI
|
|||
static std::once_flag outputFlag;
|
||||
std::call_once(outputFlag, initialize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,8 @@ public:
|
|||
void resetDevice(bool contextIsHMD, const QString& device);
|
||||
|
||||
signals:
|
||||
void deviceSelected(const QAudioDeviceInfo& device);
|
||||
void deviceSelected(const QAudioDeviceInfo& device = QAudioDeviceInfo(),
|
||||
const QAudioDeviceInfo& previousDevice = QAudioDeviceInfo());
|
||||
void deviceChanged(const QAudioDeviceInfo& device);
|
||||
|
||||
private slots:
|
||||
|
@ -53,6 +54,8 @@ private slots:
|
|||
private:
|
||||
friend class AudioDevices;
|
||||
|
||||
bool setDevice(int index, bool fromUser);
|
||||
|
||||
static QHash<int, QByteArray> _roles;
|
||||
static Qt::ItemFlags _flags;
|
||||
|
||||
|
@ -76,8 +79,7 @@ signals:
|
|||
|
||||
private slots:
|
||||
void onContextChanged(const QString& context);
|
||||
void onInputDeviceSelected(const QAudioDeviceInfo& device);
|
||||
void onOutputDeviceSelected(const QAudioDeviceInfo& device);
|
||||
void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice);
|
||||
void onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device);
|
||||
void onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices);
|
||||
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
//
|
||||
// Created by Anthony J. Thibault on 2016-12-12
|
||||
// Copyright 2013-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
|
||||
//
|
||||
|
||||
#ifndef hifi_QmlWrapper_h
|
||||
#define hifi_QmlWrapper_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <OffscreenUi.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
class QmlWrapper : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QmlWrapper(QObject* qmlObject, QObject* parent = nullptr)
|
||||
: QObject(parent), _qmlObject(qmlObject) {
|
||||
}
|
||||
|
||||
Q_INVOKABLE void writeProperty(QString propertyName, QVariant propertyValue) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->executeOnUiThread([=] {
|
||||
_qmlObject->setProperty(propertyName.toStdString().c_str(), propertyValue);
|
||||
});
|
||||
}
|
||||
|
||||
Q_INVOKABLE void writeProperties(QVariant propertyMap) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->executeOnUiThread([=] {
|
||||
QVariantMap map = propertyMap.toMap();
|
||||
for (const QString& key : map.keys()) {
|
||||
_qmlObject->setProperty(key.toStdString().c_str(), map[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Q_INVOKABLE QVariant readProperty(const QString& propertyName) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->returnFromUiThread([&]()->QVariant {
|
||||
return _qmlObject->property(propertyName.toStdString().c_str());
|
||||
});
|
||||
}
|
||||
|
||||
Q_INVOKABLE QVariant readProperties(const QVariant& propertyList) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->returnFromUiThread([&]()->QVariant {
|
||||
QVariantMap result;
|
||||
for (const QVariant& property : propertyList.toList()) {
|
||||
QString propertyString = property.toString();
|
||||
result.insert(propertyString, _qmlObject->property(propertyString.toStdString().c_str()));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
QObject* _qmlObject{ nullptr };
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,104 +0,0 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016-06-16
|
||||
// Copyright 2013-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 "ToolbarScriptingInterface.h"
|
||||
|
||||
|
||||
#include <QtCore/QThread>
|
||||
|
||||
#include <OffscreenUi.h>
|
||||
#include "QmlWrapper.h"
|
||||
|
||||
class ToolbarButtonProxy : public QmlWrapper {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ToolbarButtonProxy(QObject* qmlObject, QObject* parent = nullptr) : QmlWrapper(qmlObject, parent) {
|
||||
std::lock_guard<std::mutex> guard(_mutex);
|
||||
_qmlButton = qobject_cast<QQuickItem*>(qmlObject);
|
||||
connect(qmlObject, SIGNAL(clicked()), this, SIGNAL(clicked()));
|
||||
}
|
||||
|
||||
Q_INVOKABLE void editProperties(QVariantMap properties) {
|
||||
std::lock_guard<std::mutex> guard(_mutex);
|
||||
QVariantMap::const_iterator iter = properties.constBegin();
|
||||
while (iter != properties.constEnd()) {
|
||||
_properties[iter.key()] = iter.value();
|
||||
if (_qmlButton) {
|
||||
// [01/25 14:26:20] [WARNING] [default] QMetaObject::invokeMethod: No such method ToolbarButton_QMLTYPE_195::changeProperty(QVariant,QVariant)
|
||||
|
||||
QMetaObject::invokeMethod(_qmlButton, "changeProperty", Qt::AutoConnection,
|
||||
Q_ARG(QVariant, QVariant(iter.key())), Q_ARG(QVariant, iter.value()));
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
mutable std::mutex _mutex;
|
||||
QQuickItem* _qmlButton { nullptr };
|
||||
QVariantMap _properties;
|
||||
};
|
||||
|
||||
class ToolbarProxy : public QmlWrapper {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ToolbarProxy(QObject* qmlObject, QObject* parent = nullptr) : QmlWrapper(qmlObject, parent) { }
|
||||
|
||||
Q_INVOKABLE QObject* addButton(const QVariant& properties) {
|
||||
QVariant resultVar;
|
||||
Qt::ConnectionType connectionType = Qt::AutoConnection;
|
||||
if (QThread::currentThread() != _qmlObject->thread()) {
|
||||
connectionType = Qt::BlockingQueuedConnection;
|
||||
}
|
||||
bool invokeResult = QMetaObject::invokeMethod(_qmlObject, "addButton", connectionType, Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, properties));
|
||||
if (!invokeResult) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QObject* rawButton = qvariant_cast<QObject *>(resultVar);
|
||||
if (!rawButton) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ToolbarButtonProxy(rawButton, this);
|
||||
}
|
||||
|
||||
Q_INVOKABLE void removeButton(const QVariant& name) {
|
||||
QMetaObject::invokeMethod(_qmlObject, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, name));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
QObject* ToolbarScriptingInterface::getToolbar(const QString& toolbarId) {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto desktop = offscreenUi->getDesktop();
|
||||
Qt::ConnectionType connectionType = Qt::AutoConnection;
|
||||
if (QThread::currentThread() != desktop->thread()) {
|
||||
connectionType = Qt::BlockingQueuedConnection;
|
||||
}
|
||||
QVariant resultVar;
|
||||
bool invokeResult = QMetaObject::invokeMethod(desktop, "getToolbar", connectionType, Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, toolbarId));
|
||||
if (!invokeResult) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QObject* rawToolbar = qvariant_cast<QObject *>(resultVar);
|
||||
if (!rawToolbar) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ToolbarProxy(rawToolbar);
|
||||
}
|
||||
|
||||
|
||||
#include "ToolbarScriptingInterface.moc"
|
|
@ -1,26 +0,0 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016-06-16
|
||||
// Copyright 2013-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
|
||||
//
|
||||
|
||||
#ifndef hifi_ToolbarScriptingInterface_h
|
||||
#define hifi_ToolbarScriptingInterface_h
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
class ToolbarProxy;
|
||||
|
||||
class ToolbarScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Q_INVOKABLE QObject* getToolbar(const QString& toolbarId);
|
||||
};
|
||||
|
||||
#endif // hifi_ToolbarScriptingInterface_h
|
|
@ -17,6 +17,7 @@
|
|||
#include <Application.h>
|
||||
#include <MainWindow.h>
|
||||
#include <PathUtils.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
|
||||
#include "AddressBarDialog.h"
|
||||
#include "ConnectionFailureDialog.h"
|
||||
|
@ -28,7 +29,6 @@
|
|||
#include "PreferencesDialog.h"
|
||||
#include "UpdateDialog.h"
|
||||
|
||||
#include "TabletScriptingInterface.h"
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
|
||||
static const QVariant TABLET_ADDRESS_DIALOG = "TabletAddressDialog.qml";
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
#include <NetworkingConstants.h>
|
||||
#include <plugins/PluginManager.h>
|
||||
#include <plugins/SteamClientPlugin.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "DependencyManager.h"
|
||||
#include "Menu.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "TabletScriptingInterface.h"
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
|
||||
HIFI_QML_DEF(LoginDialog)
|
||||
|
|
|
@ -25,16 +25,15 @@
|
|||
#include <GeometryCache.h>
|
||||
#include <GeometryUtil.h>
|
||||
#include <scripting/HMDScriptingInterface.h>
|
||||
#include <gl/OffscreenQmlSurface.h>
|
||||
#include <ui/OffscreenQmlSurface.h>
|
||||
#include <ui/OffscreenQmlSurfaceCache.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
#include <PathUtils.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <TabletScriptingInterface.h>
|
||||
#include <TextureCache.h>
|
||||
#include <UsersScriptingInterface.h>
|
||||
#include <UserActivityLoggerScriptingInterface.h>
|
||||
#include <AbstractViewStateInterface.h>
|
||||
#include <gl/OffscreenQmlSurface.h>
|
||||
#include <gl/OffscreenQmlSurfaceCache.h>
|
||||
#include <AddressManager.h>
|
||||
#include "scripting/AccountScriptingInterface.h"
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
|
@ -87,7 +86,7 @@ Web3DOverlay::~Web3DOverlay() {
|
|||
|
||||
if (rootItem && rootItem->objectName() == "tabletRoot") {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr, nullptr);
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr);
|
||||
}
|
||||
|
||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
|
@ -206,7 +205,7 @@ void Web3DOverlay::loadSourceURL() {
|
|||
_webSurface->getSurfaceContext()->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
|
||||
|
||||
_webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../");
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data());
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data());
|
||||
|
||||
// mark the TabletProxy object as cpp ownership.
|
||||
QObject* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
|
|
@ -239,10 +239,11 @@ static void FIR_1x4_SSE(float* src, float* dst0, float* dst1, float* dst2, float
|
|||
#include "CPUDetect.h"
|
||||
|
||||
void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
|
||||
void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
|
||||
|
||||
static void FIR_1x4(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) {
|
||||
|
||||
static auto f = cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE;
|
||||
static auto f = cpuSupportsAVX512() ? FIR_1x4_AVX512 : (cpuSupportsAVX2() ? FIR_1x4_AVX2 : FIR_1x4_SSE);
|
||||
(*f)(src, dst0, dst1, dst2, dst3, coef, numFrames); // dispatch
|
||||
}
|
||||
|
||||
|
|
|
@ -9,15 +9,11 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
|
||||
#ifdef __AVX2__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <immintrin.h> // AVX2
|
||||
|
||||
#ifndef __AVX2__
|
||||
#error Must be compiled with /arch:AVX2 or -mavx2 -mfma.
|
||||
#endif
|
||||
#include <immintrin.h>
|
||||
|
||||
#define _mm256_permute4x64_ps(ymm, imm) _mm256_castpd_ps(_mm256_permute4x64_pd(_mm256_castps_pd(ymm), imm));
|
||||
|
||||
|
|
|
@ -9,17 +9,13 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
|
||||
#ifdef __AVX2__
|
||||
|
||||
#include <assert.h>
|
||||
#include <immintrin.h> // AVX2
|
||||
#include <immintrin.h>
|
||||
|
||||
#include "../AudioHRTF.h"
|
||||
|
||||
#ifndef __AVX2__
|
||||
#error Must be compiled with /arch:AVX2 or -mavx2 -mfma.
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
// for some reason, GCC -O2 results in poorly optimized code
|
||||
#pragma GCC optimize("Os")
|
||||
|
|
|
@ -9,17 +9,13 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
|
||||
#ifdef __AVX2__
|
||||
|
||||
#include <assert.h>
|
||||
#include <immintrin.h>
|
||||
|
||||
#include "../AudioSRC.h"
|
||||
|
||||
#ifndef __AVX2__
|
||||
#error Must be compiled with /arch:AVX2 or -mavx2 -mfma.
|
||||
#endif
|
||||
|
||||
// high/low part of int64_t
|
||||
#define LO32(a) ((uint32_t)(a))
|
||||
#define HI32(a) ((int32_t)((a) >> 32))
|
||||
|
|
101
libraries/audio/src/avx512/AudioHRTF_avx512.cpp
Normal file
101
libraries/audio/src/avx512/AudioHRTF_avx512.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// AudioHRTF_avx512.cpp
|
||||
// libraries/audio/src
|
||||
//
|
||||
// Created by Ken Cooke on 6/20/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#if defined(__AVX512F__)
|
||||
|
||||
#include <assert.h>
|
||||
#include <immintrin.h>
|
||||
|
||||
#include "../AudioHRTF.h"
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
// for some reason, GCC -O2 results in poorly optimized code
|
||||
#pragma GCC optimize("Os")
|
||||
#endif
|
||||
|
||||
// 1 channel input, 4 channel output
|
||||
void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) {
|
||||
|
||||
float* coef0 = coef[0] + HRTF_TAPS - 1; // process backwards
|
||||
float* coef1 = coef[1] + HRTF_TAPS - 1;
|
||||
float* coef2 = coef[2] + HRTF_TAPS - 1;
|
||||
float* coef3 = coef[3] + HRTF_TAPS - 1;
|
||||
|
||||
assert(numFrames % 16 == 0);
|
||||
|
||||
for (int i = 0; i < numFrames; i += 16) {
|
||||
|
||||
__m512 acc0 = _mm512_setzero_ps();
|
||||
__m512 acc1 = _mm512_setzero_ps();
|
||||
__m512 acc2 = _mm512_setzero_ps();
|
||||
__m512 acc3 = _mm512_setzero_ps();
|
||||
__m512 acc4 = _mm512_setzero_ps();
|
||||
__m512 acc5 = _mm512_setzero_ps();
|
||||
__m512 acc6 = _mm512_setzero_ps();
|
||||
__m512 acc7 = _mm512_setzero_ps();
|
||||
|
||||
float* ps = &src[i - HRTF_TAPS + 1]; // process forwards
|
||||
|
||||
assert(HRTF_TAPS % 4 == 0);
|
||||
|
||||
for (int k = 0; k < HRTF_TAPS; k += 4) {
|
||||
|
||||
__m512 x0 = _mm512_loadu_ps(&ps[k+0]);
|
||||
acc0 = _mm512_fmadd_ps(_mm512_set1_ps(coef0[-k-0]), x0, acc0); // vfmadd231ps acc0, x0, dword ptr [coef]{1to16}
|
||||
acc1 = _mm512_fmadd_ps(_mm512_set1_ps(coef1[-k-0]), x0, acc1);
|
||||
acc2 = _mm512_fmadd_ps(_mm512_set1_ps(coef2[-k-0]), x0, acc2);
|
||||
acc3 = _mm512_fmadd_ps(_mm512_set1_ps(coef3[-k-0]), x0, acc3);
|
||||
|
||||
__m512 x1 = _mm512_loadu_ps(&ps[k+1]);
|
||||
acc4 = _mm512_fmadd_ps(_mm512_set1_ps(coef0[-k-1]), x1, acc4);
|
||||
acc5 = _mm512_fmadd_ps(_mm512_set1_ps(coef1[-k-1]), x1, acc5);
|
||||
acc6 = _mm512_fmadd_ps(_mm512_set1_ps(coef2[-k-1]), x1, acc6);
|
||||
acc7 = _mm512_fmadd_ps(_mm512_set1_ps(coef3[-k-1]), x1, acc7);
|
||||
|
||||
__m512 x2 = _mm512_loadu_ps(&ps[k+2]);
|
||||
acc0 = _mm512_fmadd_ps(_mm512_set1_ps(coef0[-k-2]), x2, acc0);
|
||||
acc1 = _mm512_fmadd_ps(_mm512_set1_ps(coef1[-k-2]), x2, acc1);
|
||||
acc2 = _mm512_fmadd_ps(_mm512_set1_ps(coef2[-k-2]), x2, acc2);
|
||||
acc3 = _mm512_fmadd_ps(_mm512_set1_ps(coef3[-k-2]), x2, acc3);
|
||||
|
||||
__m512 x3 = _mm512_loadu_ps(&ps[k+3]);
|
||||
acc4 = _mm512_fmadd_ps(_mm512_set1_ps(coef0[-k-3]), x3, acc4);
|
||||
acc5 = _mm512_fmadd_ps(_mm512_set1_ps(coef1[-k-3]), x3, acc5);
|
||||
acc6 = _mm512_fmadd_ps(_mm512_set1_ps(coef2[-k-3]), x3, acc6);
|
||||
acc7 = _mm512_fmadd_ps(_mm512_set1_ps(coef3[-k-3]), x3, acc7);
|
||||
}
|
||||
|
||||
acc0 = _mm512_add_ps(acc0, acc4);
|
||||
acc1 = _mm512_add_ps(acc1, acc5);
|
||||
acc2 = _mm512_add_ps(acc2, acc6);
|
||||
acc3 = _mm512_add_ps(acc3, acc7);
|
||||
|
||||
_mm512_storeu_ps(&dst0[i], acc0);
|
||||
_mm512_storeu_ps(&dst1[i], acc1);
|
||||
_mm512_storeu_ps(&dst2[i], acc2);
|
||||
_mm512_storeu_ps(&dst3[i], acc3);
|
||||
}
|
||||
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
|
||||
// FIXME: this fallback can be removed, once we require VS2017
|
||||
#elif defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
#include "../AudioHRTF.h"
|
||||
|
||||
void FIR_1x4_AVX2(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames);
|
||||
|
||||
void FIR_1x4_AVX512(float* src, float* dst0, float* dst1, float* dst2, float* dst3, float coef[4][HRTF_TAPS], int numFrames) {
|
||||
FIR_1x4_AVX2(src, dst0, dst1, dst2, dst3, coef, numFrames);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1496,13 +1496,13 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide
|
|||
udt::SequenceNumber incomingSequenceNumber(incomingSequenceNumberType);
|
||||
|
||||
if (!_hasProcessedFirstIdentity) {
|
||||
_lastSequenceNumber = incomingSequenceNumber - 1;
|
||||
_identitySequenceNumber = incomingSequenceNumber - 1;
|
||||
_hasProcessedFirstIdentity = true;
|
||||
qCDebug(avatars) << "Processing first identity packet for" << avatarSessionID << "-"
|
||||
<< (udt::SequenceNumber::Type) incomingSequenceNumber;
|
||||
}
|
||||
|
||||
if (incomingSequenceNumber > _lastSequenceNumber) {
|
||||
if (incomingSequenceNumber > _identitySequenceNumber) {
|
||||
Identity identity;
|
||||
|
||||
packetStream >> identity.skeletonModelURL
|
||||
|
@ -1512,7 +1512,7 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide
|
|||
>> identity.avatarEntityData;
|
||||
|
||||
// set the store identity sequence number to match the incoming identity
|
||||
_lastSequenceNumber = incomingSequenceNumber;
|
||||
_identitySequenceNumber = incomingSequenceNumber;
|
||||
|
||||
if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) {
|
||||
setSkeletonModelURL(identity.skeletonModelURL);
|
||||
|
@ -1555,7 +1555,7 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide
|
|||
} else {
|
||||
|
||||
qCDebug(avatars) << "Refusing to process identity for" << uuidStringWithoutCurlyBraces(avatarSessionID) << "since"
|
||||
<< (udt::SequenceNumber::Type) _lastSequenceNumber
|
||||
<< (udt::SequenceNumber::Type) _identitySequenceNumber
|
||||
<< "is >=" << (udt::SequenceNumber::Type) incomingSequenceNumber;
|
||||
#endif
|
||||
}
|
||||
|
@ -1571,7 +1571,7 @@ QByteArray AvatarData::identityByteArray() const {
|
|||
|
||||
_avatarEntitiesLock.withReadLock([&] {
|
||||
identityStream << getSessionUUID()
|
||||
<< (udt::SequenceNumber::Type) _lastSequenceNumber
|
||||
<< (udt::SequenceNumber::Type) _identitySequenceNumber
|
||||
<< urlToSend
|
||||
<< _attachmentData
|
||||
<< _displayName
|
||||
|
@ -1755,7 +1755,7 @@ void AvatarData::sendIdentityPacket() {
|
|||
|
||||
if (_identityDataChanged) {
|
||||
// if the identity data has changed, push the sequence number forwards
|
||||
++_lastSequenceNumber;
|
||||
++_identitySequenceNumber;
|
||||
}
|
||||
|
||||
QByteArray identityData = identityByteArray();
|
||||
|
|
|
@ -625,6 +625,8 @@ public:
|
|||
bool getIdentityDataChanged() const { return _identityDataChanged; } // has the identity data changed since the last time sendIdentityPacket() was called
|
||||
void markIdentityDataChanged() { _identityDataChanged = true; }
|
||||
|
||||
void pushIdentitySequenceNumber() { ++_identitySequenceNumber; };
|
||||
|
||||
float getDensity() const { return _density; }
|
||||
|
||||
signals:
|
||||
|
@ -781,7 +783,7 @@ protected:
|
|||
float _audioAverageLoudness { 0.0f };
|
||||
|
||||
bool _identityDataChanged { false };
|
||||
udt::SequenceNumber _lastSequenceNumber { 0 };
|
||||
udt::SequenceNumber _identitySequenceNumber { 0 };
|
||||
bool _hasProcessedFirstIdentity { false };
|
||||
float _density;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
set(TARGET_NAME entities-renderer)
|
||||
AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils)
|
||||
setup_hifi_library(Widgets Network Script)
|
||||
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image)
|
||||
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image ui)
|
||||
include_hifi_library_headers(networking)
|
||||
include_hifi_library_headers(gl)
|
||||
include_hifi_library_headers(ktx)
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include <PathUtils.h>
|
||||
#include <TextureCache.h>
|
||||
#include <gpu/Context.h>
|
||||
#include <TabletScriptingInterface.h>
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
|
||||
#include "EntityTreeRenderer.h"
|
||||
#include "EntitiesRendererLogging.h"
|
||||
|
@ -74,19 +74,6 @@ bool RenderableWebEntityItem::buildWebSurface(QSharedPointer<EntityTreeRenderer>
|
|||
qWarning() << "Too many concurrent web views to create new view";
|
||||
return false;
|
||||
}
|
||||
QString javaScriptToInject;
|
||||
QFile webChannelFile(":qtwebchannel/qwebchannel.js");
|
||||
QFile createGlobalEventBridgeFile(PathUtils::resourcesPath() + "/html/createGlobalEventBridge.js");
|
||||
if (webChannelFile.open(QFile::ReadOnly | QFile::Text) &&
|
||||
createGlobalEventBridgeFile.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QString webChannelStr = QTextStream(&webChannelFile).readAll();
|
||||
QString createGlobalEventBridgeStr = QTextStream(&createGlobalEventBridgeFile).readAll();
|
||||
|
||||
// concatenate these js files
|
||||
_javaScriptToInject = webChannelStr + createGlobalEventBridgeStr;
|
||||
} else {
|
||||
qCWarning(entitiesrenderer) << "unable to find qwebchannel.js or createGlobalEventBridge.js";
|
||||
}
|
||||
|
||||
// Save the original GL context, because creating a QML surface will create a new context
|
||||
QOpenGLContext* currentContext = QOpenGLContext::currentContext();
|
||||
|
@ -266,10 +253,7 @@ void RenderableWebEntityItem::loadSourceURL() {
|
|||
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
|
||||
}
|
||||
|
||||
_webSurface->load("WebEntityView.qml", [&](QQmlContext* context, QObject* obj) {
|
||||
context->setContextProperty("eventBridgeJavaScriptToInject", QVariant(_javaScriptToInject));
|
||||
});
|
||||
|
||||
_webSurface->load("WebEntityView.qml");
|
||||
_webSurface->getRootItem()->setProperty("url", _sourceUrl);
|
||||
_webSurface->getSurfaceContext()->setContextProperty("desktop", QVariant());
|
||||
|
||||
|
@ -280,8 +264,7 @@ void RenderableWebEntityItem::loadSourceURL() {
|
|||
|
||||
if (_webSurface->getRootItem() && _webSurface->getRootItem()->objectName() == "tabletRoot") {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system",
|
||||
_webSurface->getRootItem(), _webSurface.data());
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data());
|
||||
}
|
||||
}
|
||||
_webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(getPosition()));
|
||||
|
@ -386,7 +369,7 @@ void RenderableWebEntityItem::destroyWebSurface() {
|
|||
|
||||
if (rootItem && rootItem->objectName() == "tabletRoot") {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr, nullptr);
|
||||
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", nullptr);
|
||||
}
|
||||
|
||||
// Fix for crash in QtWebEngineCore when rapidly switching domains
|
||||
|
|
|
@ -13,10 +13,9 @@
|
|||
#include <QMouseEvent>
|
||||
#include <QTouchEvent>
|
||||
#include <PointerEvent.h>
|
||||
#include <gl/OffscreenQmlSurface.h>
|
||||
#include <ui/OffscreenQmlSurface.h>
|
||||
|
||||
#include <WebEntityItem.h>
|
||||
#include <gl/OffscreenQmlSurface.h>
|
||||
|
||||
#include "RenderableEntityItem.h"
|
||||
|
||||
|
@ -74,8 +73,6 @@ private:
|
|||
QMetaObject::Connection _mouseMoveConnection;
|
||||
QMetaObject::Connection _hoverLeaveConnection;
|
||||
|
||||
QString _javaScriptToInject;
|
||||
|
||||
enum contentType {
|
||||
htmlContent,
|
||||
qmlContent
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include <QThreadPool>
|
||||
|
||||
#include <Gzip.h>
|
||||
|
||||
#include "ModelNetworkingLogging.h"
|
||||
#include <Trace.h>
|
||||
#include <StatTracker.h>
|
||||
|
@ -171,7 +173,9 @@ void GeometryReader::run() {
|
|||
|
||||
QString urlname = _url.path().toLower();
|
||||
if (!urlname.isEmpty() && !_url.path().isEmpty() &&
|
||||
(_url.path().toLower().endsWith(".fbx") || _url.path().toLower().endsWith(".obj"))) {
|
||||
(_url.path().toLower().endsWith(".fbx") ||
|
||||
_url.path().toLower().endsWith(".obj") ||
|
||||
_url.path().toLower().endsWith(".obj.gz"))) {
|
||||
FBXGeometry::Pointer fbxGeometry;
|
||||
|
||||
if (_url.path().toLower().endsWith(".fbx")) {
|
||||
|
@ -181,7 +185,15 @@ void GeometryReader::run() {
|
|||
}
|
||||
} else if (_url.path().toLower().endsWith(".obj")) {
|
||||
fbxGeometry.reset(OBJReader().readOBJ(_data, _mapping, _combineParts, _url));
|
||||
} else {
|
||||
} else if (_url.path().toLower().endsWith(".obj.gz")) {
|
||||
QByteArray uncompressedData;
|
||||
if (gunzip(_data, uncompressedData)){
|
||||
fbxGeometry.reset(OBJReader().readOBJ(uncompressedData, _mapping, _combineParts, _url));
|
||||
} else {
|
||||
throw QString("failed to decompress .obj.gz" );
|
||||
}
|
||||
|
||||
} else {
|
||||
throw QString("unsupported format");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
set(TARGET_NAME networking)
|
||||
setup_hifi_library(Network WebEngine)
|
||||
setup_hifi_library(Network)
|
||||
link_hifi_libraries(shared)
|
||||
|
||||
target_include_directories(${TARGET_NAME} PRIVATE "${CMAKE_BINARY_DIR}/includes")
|
||||
|
|
|
@ -257,8 +257,40 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
|
|||
PacketType headerType = NLPacket::typeInHeader(packet);
|
||||
|
||||
if (NON_SOURCED_PACKETS.contains(headerType)) {
|
||||
emit dataReceived(NodeType::Unassigned, packet.getPayloadSize());
|
||||
return true;
|
||||
if (REPLICATED_PACKET_MAPPING.key(headerType) != PacketType::Unknown) {
|
||||
// this is a replicated packet type - make sure the socket that sent it to us matches
|
||||
// one from one of our current upstream nodes
|
||||
|
||||
NodeType_t sendingNodeType { NodeType::Unassigned };
|
||||
|
||||
eachNodeBreakable([&packet, &sendingNodeType](const SharedNodePointer& node){
|
||||
if (NodeType::isUpstream(node->getType()) && node->getPublicSocket() == packet.getSenderSockAddr()) {
|
||||
sendingNodeType = node->getType();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (sendingNodeType != NodeType::Unassigned) {
|
||||
emit dataReceived(sendingNodeType, packet.getPayloadSize());
|
||||
return true;
|
||||
} else {
|
||||
static const QString UNSOLICITED_REPLICATED_REGEX =
|
||||
"Replicated packet of type \\d+ \\([\\sa-zA-Z:]+\\) received from unknown upstream";
|
||||
static QString repeatedMessage
|
||||
= LogHandler::getInstance().addRepeatedMessageRegex(UNSOLICITED_REPLICATED_REGEX);
|
||||
|
||||
qCDebug(networking) << "Replicated packet of type" << headerType
|
||||
<< "received from unknown upstream" << packet.getSenderSockAddr();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
emit dataReceived(NodeType::Unassigned, packet.getPayloadSize());
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
QUuid sourceID = NLPacket::sourceIDInHeader(packet);
|
||||
|
||||
|
@ -583,14 +615,14 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t
|
|||
matchingNode->setPermissions(permissions);
|
||||
matchingNode->setConnectionSecret(connectionSecret);
|
||||
matchingNode->setIsReplicated(isReplicated);
|
||||
matchingNode->setIsUpstream(isUpstream);
|
||||
matchingNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType));
|
||||
|
||||
return matchingNode;
|
||||
} else {
|
||||
// we didn't have this node, so add them
|
||||
Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket);
|
||||
newNode->setIsReplicated(isReplicated);
|
||||
newNode->setIsUpstream(isUpstream);
|
||||
newNode->setIsUpstream(isUpstream || NodeType::isUpstream(nodeType));
|
||||
newNode->setConnectionSecret(connectionSecret);
|
||||
newNode->setPermissions(permissions);
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ void NodeType::init() {
|
|||
TypeNameHash.insert(NodeType::MessagesMixer, "Messages Mixer");
|
||||
TypeNameHash.insert(NodeType::AssetServer, "Asset Server");
|
||||
TypeNameHash.insert(NodeType::EntityScriptServer, "Entity Script Server");
|
||||
TypeNameHash.insert(NodeType::UpstreamAudioMixer, "Upstream Audio Mixer");
|
||||
TypeNameHash.insert(NodeType::UpstreamAvatarMixer, "Upstream Avatar Mixer");
|
||||
TypeNameHash.insert(NodeType::DownstreamAudioMixer, "Downstream Audio Mixer");
|
||||
TypeNameHash.insert(NodeType::DownstreamAvatarMixer, "Downstream Avatar Mixer");
|
||||
TypeNameHash.insert(NodeType::Unassigned, "Unassigned");
|
||||
|
@ -52,8 +54,23 @@ const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
|
|||
return matchedTypeName != TypeNameHash.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME;
|
||||
}
|
||||
|
||||
bool NodeType::isUpstream(NodeType_t nodeType) {
|
||||
return nodeType == NodeType::UpstreamAudioMixer || nodeType == NodeType::UpstreamAvatarMixer;
|
||||
}
|
||||
|
||||
bool NodeType::isDownstream(NodeType_t nodeType) {
|
||||
return nodeType == NodeType::DownstreamAudioMixer || nodeType == NodeType::DownstreamAvatarMixer;
|
||||
return nodeType == NodeType::DownstreamAudioMixer || nodeType == NodeType::DownstreamAvatarMixer;
|
||||
}
|
||||
|
||||
NodeType_t NodeType::upstreamType(NodeType_t primaryType) {
|
||||
switch (primaryType) {
|
||||
case AudioMixer:
|
||||
return UpstreamAudioMixer;
|
||||
case AvatarMixer:
|
||||
return UpstreamAvatarMixer;
|
||||
default:
|
||||
return Unassigned;
|
||||
}
|
||||
}
|
||||
|
||||
NodeType_t NodeType::downstreamType(NodeType_t primaryType) {
|
||||
|
|
|
@ -669,9 +669,11 @@ void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
|
|||
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket,
|
||||
nodeLocalSocket, isReplicated, false, connectionUUID, permissions);
|
||||
|
||||
// nodes that are downstream of our own type are kept alive when we hear about them from the domain server
|
||||
if (node->getType() == NodeType::downstreamType(_ownerType)) {
|
||||
// nodes that are downstream or upstream of our own type are kept alive when we hear about them from the domain server
|
||||
// and always have their public socket as their active socket
|
||||
if (node->getType() == NodeType::downstreamType(_ownerType) || node->getType() == NodeType::upstreamType(_ownerType)) {
|
||||
node->setLastHeardMicrostamp(usecTimestampNow());
|
||||
node->activatePublicSocket();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ namespace NodeType {
|
|||
const NodeType_t AssetServer = 'A';
|
||||
const NodeType_t MessagesMixer = 'm';
|
||||
const NodeType_t EntityScriptServer = 'S';
|
||||
const NodeType_t UpstreamAudioMixer = 'B';
|
||||
const NodeType_t UpstreamAvatarMixer = 'C';
|
||||
const NodeType_t DownstreamAudioMixer = 'a';
|
||||
const NodeType_t DownstreamAvatarMixer = 'w';
|
||||
const NodeType_t Unassigned = 1;
|
||||
|
@ -32,9 +34,12 @@ namespace NodeType {
|
|||
void init();
|
||||
|
||||
const QString& getNodeTypeName(NodeType_t nodeType);
|
||||
bool isUpstream(NodeType_t nodeType);
|
||||
bool isDownstream(NodeType_t nodeType);
|
||||
NodeType_t upstreamType(NodeType_t primaryType);
|
||||
NodeType_t downstreamType(NodeType_t primaryType);
|
||||
|
||||
|
||||
NodeType_t fromString(QString type);
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ const QHash<PacketType, PacketType> REPLICATED_PACKET_MAPPING {
|
|||
{ PacketType::SilentAudioFrame, PacketType::ReplicatedSilentAudioFrame },
|
||||
{ PacketType::AvatarIdentity, PacketType::ReplicatedAvatarIdentity },
|
||||
{ PacketType::KillAvatar, PacketType::ReplicatedKillAvatar },
|
||||
{ PacketType::BulkAvatarData, PacketType::ReplicatedBulkAvatarData }
|
||||
};
|
||||
|
||||
PacketVersion versionForPacketType(PacketType packetType) {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
enum class PluginType {
|
||||
DISPLAY_PLUGIN,
|
||||
|
@ -26,8 +27,12 @@ class PluginManager;
|
|||
|
||||
using DisplayPluginPointer = std::shared_ptr<DisplayPlugin>;
|
||||
using DisplayPluginList = std::vector<DisplayPluginPointer>;
|
||||
using DisplayPluginProvider = std::function<DisplayPluginList()>;
|
||||
using InputPluginPointer = std::shared_ptr<InputPlugin>;
|
||||
using InputPluginList = std::vector<InputPluginPointer>;
|
||||
using InputPluginProvider = std::function<InputPluginList()>;
|
||||
using CodecPluginPointer = std::shared_ptr<CodecPlugin>;
|
||||
using CodecPluginList = std::vector<CodecPluginPointer>;
|
||||
using CodecPluginProvider = std::function<CodecPluginList()>;
|
||||
using SteamClientPluginPointer = std::shared_ptr<SteamClientPlugin>;
|
||||
using InputPluginSettingsPersister = std::function<void(const InputPluginList&)>;
|
||||
|
|
|
@ -23,6 +23,26 @@
|
|||
#include "InputPlugin.h"
|
||||
#include "PluginLogging.h"
|
||||
|
||||
DisplayPluginProvider PluginManager::_displayPluginProvider = []()->DisplayPluginList { return {}; };
|
||||
InputPluginProvider PluginManager::_inputPluginProvider = []()->InputPluginList { return {}; };
|
||||
CodecPluginProvider PluginManager::_codecPluginProvider = []()->CodecPluginList { return {}; };
|
||||
InputPluginSettingsPersister PluginManager::_inputSettingsPersister = [](const InputPluginList& list) {};
|
||||
|
||||
void PluginManager::setDisplayPluginProvider(const DisplayPluginProvider& provider) {
|
||||
_displayPluginProvider = provider;
|
||||
}
|
||||
|
||||
void PluginManager::setInputPluginProvider(const InputPluginProvider& provider) {
|
||||
_inputPluginProvider = provider;
|
||||
}
|
||||
|
||||
void PluginManager::setCodecPluginProvider(const CodecPluginProvider& provider) {
|
||||
_codecPluginProvider = provider;
|
||||
}
|
||||
|
||||
void PluginManager::setInputPluginSettingsPersister(const InputPluginSettingsPersister& persister) {
|
||||
_inputSettingsPersister = persister;
|
||||
}
|
||||
|
||||
PluginManager* PluginManager::getInstance() {
|
||||
static PluginManager _manager;
|
||||
|
@ -117,12 +137,12 @@ const LoaderList& getLoadedPlugins() {
|
|||
PluginManager::PluginManager() {
|
||||
}
|
||||
|
||||
extern CodecPluginList getCodecPlugins();
|
||||
|
||||
const CodecPluginList& PluginManager::getCodecPlugins() {
|
||||
static CodecPluginList codecPlugins;
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
codecPlugins = _codecPluginProvider();
|
||||
|
||||
// Now grab the dynamic plugins
|
||||
for (auto loader : getLoadedPlugins()) {
|
||||
CodecProvider* codecProvider = qobject_cast<CodecProvider*>(loader->instance());
|
||||
|
@ -163,11 +183,6 @@ const SteamClientPluginPointer PluginManager::getSteamClientPlugin() {
|
|||
|
||||
#ifndef Q_OS_ANDROID
|
||||
|
||||
// TODO migrate to a DLL model where plugins are discovered and loaded at runtime by the PluginManager class
|
||||
extern DisplayPluginList getDisplayPlugins();
|
||||
extern InputPluginList getInputPlugins();
|
||||
|
||||
extern void saveInputPluginSettings(const InputPluginList& plugins);
|
||||
static DisplayPluginList displayPlugins;
|
||||
|
||||
const DisplayPluginList& PluginManager::getDisplayPlugins() {
|
||||
|
@ -183,7 +198,7 @@ const DisplayPluginList& PluginManager::getDisplayPlugins() {
|
|||
|
||||
std::call_once(once, [&] {
|
||||
// Grab the built in plugins
|
||||
displayPlugins = ::getDisplayPlugins();
|
||||
displayPlugins = _displayPluginProvider();
|
||||
|
||||
|
||||
// Now grab the dynamic plugins
|
||||
|
@ -229,7 +244,7 @@ const InputPluginList& PluginManager::getInputPlugins() {
|
|||
};
|
||||
|
||||
std::call_once(once, [&] {
|
||||
inputPlugins = ::getInputPlugins();
|
||||
inputPlugins = _inputPluginProvider();
|
||||
|
||||
// Now grab the dynamic plugins
|
||||
for (auto loader : getLoadedPlugins()) {
|
||||
|
@ -288,7 +303,7 @@ void PluginManager::disableInputs(const QStringList& inputs) {
|
|||
}
|
||||
|
||||
void PluginManager::saveSettings() {
|
||||
saveInputPluginSettings(getInputPlugins());
|
||||
_inputSettingsPersister(getInputPlugins());
|
||||
}
|
||||
|
||||
void PluginManager::shutdown() {
|
||||
|
|
|
@ -31,6 +31,18 @@ public:
|
|||
void setContainer(PluginContainer* container) { _container = container; }
|
||||
|
||||
void shutdown();
|
||||
|
||||
// Application that have statically linked plugins can expose them to the plugin manager with these function
|
||||
static void setDisplayPluginProvider(const DisplayPluginProvider& provider);
|
||||
static void setInputPluginProvider(const InputPluginProvider& provider);
|
||||
static void setCodecPluginProvider(const CodecPluginProvider& provider);
|
||||
static void setInputPluginSettingsPersister(const InputPluginSettingsPersister& persister);
|
||||
|
||||
private:
|
||||
static DisplayPluginProvider _displayPluginProvider;
|
||||
static InputPluginProvider _inputPluginProvider;
|
||||
static CodecPluginProvider _codecPluginProvider;
|
||||
static InputPluginSettingsPersister _inputSettingsPersister;
|
||||
|
||||
PluginContainer* _container { nullptr };
|
||||
};
|
||||
|
|
|
@ -16,6 +16,6 @@ if (NOT ANDROID)
|
|||
|
||||
endif ()
|
||||
|
||||
link_hifi_libraries(shared networking octree gpu ui procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image)
|
||||
link_hifi_libraries(shared networking octree gpu procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image)
|
||||
# ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit
|
||||
include_hifi_library_headers(gl)
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <ui/Menu.h>
|
||||
#include "KeyEvent.h"
|
||||
|
||||
|
||||
|
@ -35,7 +34,7 @@ public:
|
|||
QKeySequence shortcutKeySequence; // this is what we actually use, it's set from one of the above
|
||||
|
||||
// location related items: in order of priority
|
||||
int position { ui::Menu::UNSPECIFIED_POSITION };
|
||||
int position { UNSPECIFIED_POSITION };
|
||||
QString beforeItem;
|
||||
QString afterItem;
|
||||
|
||||
|
@ -45,6 +44,9 @@ public:
|
|||
bool isSeparator { false };
|
||||
|
||||
QString grouping; /// Either: "", "Advanced", or "Developer"
|
||||
|
||||
private:
|
||||
static const int UNSPECIFIED_POSITION = -1;
|
||||
};
|
||||
Q_DECLARE_METATYPE(MenuItemProperties)
|
||||
QScriptValue menuItemPropertiesToScriptValue(QScriptEngine* engine, const MenuItemProperties& props);
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QMenuBar>
|
||||
#include <QtWidgets/QMenu>
|
||||
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
@ -48,7 +50,6 @@
|
|||
#include <ScriptAvatarData.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <UUID.h>
|
||||
#include <ui/Menu.h>
|
||||
|
||||
#include <controllers/ScriptingInterface.h>
|
||||
#include <AnimationObject.h>
|
||||
|
@ -70,7 +71,6 @@
|
|||
#include "WebSocketClass.h"
|
||||
#include "RecordingScriptingInterface.h"
|
||||
#include "ScriptEngines.h"
|
||||
#include "TabletScriptingInterface.h"
|
||||
#include "ModelScriptingInterface.h"
|
||||
|
||||
|
||||
|
@ -172,7 +172,7 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const
|
|||
emit unhandledException(exception);
|
||||
}
|
||||
}, Qt::DirectConnection);
|
||||
|
||||
|
||||
setProcessEventsInterval(MSECS_PER_SECOND);
|
||||
if (isEntityServerScript()) {
|
||||
qCDebug(scriptengine) << "isEntityServerScript() -- limiting maxRetries to 1";
|
||||
|
@ -281,7 +281,7 @@ void ScriptEngine::runDebuggable() {
|
|||
scriptDebugMenu = nullptr;
|
||||
}
|
||||
}
|
||||
disconnect(timer);
|
||||
disconnect(timer);
|
||||
});
|
||||
|
||||
connect(timer, &QTimer::timeout, [this, timer] {
|
||||
|
@ -340,7 +340,7 @@ void ScriptEngine::runInThread() {
|
|||
QThread* workerThread = new QThread();
|
||||
workerThread->setObjectName(QString("js:") + getFilename().replace("about:",""));
|
||||
moveToThread(workerThread);
|
||||
|
||||
|
||||
// NOTE: If you connect any essential signals for proper shutdown or cleanup of
|
||||
// the script engine, make sure to add code to "reconnect" them to the
|
||||
// disconnectNonEssentialSignals() method
|
||||
|
@ -697,8 +697,6 @@ void ScriptEngine::init() {
|
|||
// constants
|
||||
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
|
||||
|
||||
registerGlobalObject("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
|
||||
qScriptRegisterMetaType(this, tabletToScriptValue, tabletFromScriptValue);
|
||||
registerGlobalObject("Assets", &_assetScriptingInterface);
|
||||
registerGlobalObject("Resources", DependencyManager::get<ResourceScriptingInterface>().data());
|
||||
|
||||
|
@ -1047,26 +1045,26 @@ void ScriptEngine::run() {
|
|||
auto beforeSleep = clock::now();
|
||||
|
||||
// Throttle to SCRIPT_FPS
|
||||
// We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will
|
||||
// We'd like to try to keep the script at a solid SCRIPT_FPS update rate. And so we will
|
||||
// calculate a sleepUntil to be the time from our start time until the original target
|
||||
// sleepUntil for this frame. This approach will allow us to "catch up" in the event
|
||||
// that some of our script udpates/frames take a little bit longer than the target average
|
||||
// sleepUntil for this frame. This approach will allow us to "catch up" in the event
|
||||
// that some of our script udpates/frames take a little bit longer than the target average
|
||||
// to execute.
|
||||
// NOTE: if we go to variable SCRIPT_FPS, then we will need to reconsider this approach
|
||||
const std::chrono::microseconds TARGET_SCRIPT_FRAME_DURATION(USECS_PER_SECOND / SCRIPT_FPS + 1);
|
||||
clock::time_point targetSleepUntil(startTime + (thisFrame++ * TARGET_SCRIPT_FRAME_DURATION));
|
||||
|
||||
// However, if our sleepUntil is not at least our average update and timer execution time
|
||||
// into the future it means our script is taking too long in its updates, and we want to
|
||||
// punish the script a little bit. So we will force the sleepUntil to be at least our
|
||||
// However, if our sleepUntil is not at least our average update and timer execution time
|
||||
// into the future it means our script is taking too long in its updates, and we want to
|
||||
// punish the script a little bit. So we will force the sleepUntil to be at least our
|
||||
// averageUpdate + averageTimerPerFrame time into the future.
|
||||
auto averageUpdate = totalUpdates / thisFrame;
|
||||
auto averageTimerPerFrame = _totalTimerExecution / thisFrame;
|
||||
auto averageTimerAndUpdate = averageUpdate + averageTimerPerFrame;
|
||||
auto sleepUntil = std::max(targetSleepUntil, beforeSleep + averageTimerAndUpdate);
|
||||
|
||||
// We don't want to actually sleep for too long, because it causes our scripts to hang
|
||||
// on shutdown and stop... so we want to loop and sleep until we've spent our time in
|
||||
// We don't want to actually sleep for too long, because it causes our scripts to hang
|
||||
// on shutdown and stop... so we want to loop and sleep until we've spent our time in
|
||||
// purgatory, constantly checking to see if our script was asked to end
|
||||
bool processedEvents = false;
|
||||
while (!_isFinished && clock::now() < sleepUntil) {
|
||||
|
@ -1399,7 +1397,7 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re
|
|||
if (!IS_THREADSAFE_INVOCATION(thread(), __FUNCTION__)) {
|
||||
return QString();
|
||||
}
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
QUrl url(moduleId);
|
||||
|
||||
auto displayId = moduleId;
|
||||
|
@ -1465,7 +1463,7 @@ QString ScriptEngine::_requireResolve(const QString& moduleId, const QString& re
|
|||
canonical.setPath(file.canonicalFilePath());
|
||||
}
|
||||
|
||||
bool disallowOutsideFiles = !defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile();
|
||||
bool disallowOutsideFiles = !PathUtils::defaultScriptsLocation().isParentOf(canonical) && !currentSandboxURL.isLocalFile();
|
||||
if (disallowOutsideFiles && !PathUtils::isDescendantOf(canonical, currentSandboxURL)) {
|
||||
return throwResolveError(makeError(message.arg(
|
||||
QString("path '%1' outside of origin script '%2' '%3'")
|
||||
|
@ -1750,7 +1748,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
|||
return;
|
||||
}
|
||||
if (DependencyManager::get<ScriptEngines>()->isStopped()) {
|
||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:"
|
||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFiles:"
|
||||
+ includeFiles.join(",") + "parent script:" + getFilename());
|
||||
return; // bail early
|
||||
}
|
||||
|
@ -1762,7 +1760,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
|||
bool isStandardLibrary = false;
|
||||
if (file.startsWith("/~/")) {
|
||||
thisURL = expandScriptUrl(QUrl::fromLocalFile(expandScriptPath(file)));
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
if (!defaultScriptsLoc.isParentOf(thisURL)) {
|
||||
scriptWarningMessage("Script.include() -- skipping" + file + "-- outside of standard libraries");
|
||||
continue;
|
||||
|
@ -1774,7 +1772,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
|||
|
||||
bool disallowOutsideFiles = thisURL.isLocalFile() && !isStandardLibrary && !currentSandboxURL.isLocalFile();
|
||||
if (disallowOutsideFiles && !PathUtils::isDescendantOf(thisURL, currentSandboxURL)) {
|
||||
scriptWarningMessage("Script.include() ignoring file path" + thisURL.toString()
|
||||
scriptWarningMessage("Script.include() ignoring file path" + thisURL.toString()
|
||||
+ "outside of original entity script" + currentSandboxURL.toString());
|
||||
} else {
|
||||
// We could also check here for CORS, but we don't yet.
|
||||
|
@ -1844,7 +1842,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
|||
|
||||
void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
|
||||
if (DependencyManager::get<ScriptEngines>()->isStopped()) {
|
||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"
|
||||
scriptWarningMessage("Script.include() while shutting down is ignored... includeFile:"
|
||||
+ includeFile + "parent script:" + getFilename());
|
||||
return; // bail early
|
||||
}
|
||||
|
@ -1862,12 +1860,12 @@ void ScriptEngine::load(const QString& loadFile) {
|
|||
return;
|
||||
}
|
||||
if (DependencyManager::get<ScriptEngines>()->isStopped()) {
|
||||
scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:"
|
||||
scriptWarningMessage("Script.load() while shutting down is ignored... loadFile:"
|
||||
+ loadFile + "parent script:" + getFilename());
|
||||
return; // bail early
|
||||
}
|
||||
if (!currentEntityIdentifier.isInvalidID()) {
|
||||
scriptWarningMessage("Script.load() from entity script is ignored... loadFile:"
|
||||
scriptWarningMessage("Script.load() from entity script is ignored... loadFile:"
|
||||
+ loadFile + "parent script:" + getFilename() + "entity: " + currentEntityIdentifier.toString());
|
||||
return; // bail early
|
||||
}
|
||||
|
@ -2548,7 +2546,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS
|
|||
qCDebug(scriptengine) << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] "
|
||||
"entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
|
||||
#endif
|
||||
|
||||
|
||||
if (HIFI_AUTOREFRESH_FILE_SCRIPTS) {
|
||||
refreshFileScript(entityID);
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
#include <UserActivityLogger.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include <OffscreenUi.h>
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
#include "ScriptEngineLogging.h"
|
||||
|
||||
|
@ -74,7 +72,7 @@ ScriptEngines::ScriptEngines(ScriptEngine::Context context)
|
|||
QUrl normalizeScriptURL(const QUrl& rawScriptURL) {
|
||||
if (rawScriptURL.scheme() == "file") {
|
||||
QUrl fullNormal = rawScriptURL;
|
||||
QUrl defaultScriptLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptLoc = PathUtils::defaultScriptsLocation();
|
||||
|
||||
// if this url is something "beneath" the default script url, replace the local path with ~
|
||||
if (fullNormal.scheme() == defaultScriptLoc.scheme() &&
|
||||
|
@ -93,7 +91,7 @@ QUrl normalizeScriptURL(const QUrl& rawScriptURL) {
|
|||
|
||||
QString expandScriptPath(const QString& rawPath) {
|
||||
QStringList splitPath = rawPath.split("/");
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
return defaultScriptsLoc.path() + "/" + splitPath.mid(2).join("/"); // 2 to skip the slashes in /~/
|
||||
}
|
||||
|
||||
|
@ -112,7 +110,7 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) {
|
|||
QFileInfo fileInfo(url.toLocalFile());
|
||||
url = QUrl::fromLocalFile(fileInfo.canonicalFilePath());
|
||||
|
||||
QUrl defaultScriptsLoc = defaultScriptsLocation();
|
||||
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
|
||||
if (!defaultScriptsLoc.isParentOf(url)) {
|
||||
qCWarning(scriptengine) << "Script.include() ignoring file path" << rawScriptURL
|
||||
<< "-- outside of standard libraries: "
|
||||
|
@ -327,6 +325,13 @@ void ScriptEngines::saveScripts() {
|
|||
return;
|
||||
}
|
||||
|
||||
// don't save scripts if we started with --scripts, as we would overwrite
|
||||
// the scripts that the user expects to be there when launched without the
|
||||
// --scripts override.
|
||||
if (_defaultScriptsLocationOverridden) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Saves all currently running user-loaded scripts
|
||||
QVariantList list;
|
||||
|
||||
|
@ -356,6 +361,13 @@ QStringList ScriptEngines::getRunningScripts() {
|
|||
void ScriptEngines::stopAllScripts(bool restart) {
|
||||
QVector<QString> toReload;
|
||||
QReadLocker lock(&_scriptEnginesHashLock);
|
||||
|
||||
if (_isReloading) {
|
||||
return;
|
||||
} else {
|
||||
_isReloading = true;
|
||||
}
|
||||
|
||||
for (QHash<QUrl, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
|
||||
it != _scriptEnginesHash.constEnd(); it++) {
|
||||
ScriptEngine *scriptEngine = it.value();
|
||||
|
@ -375,7 +387,7 @@ void ScriptEngines::stopAllScripts(bool restart) {
|
|||
}
|
||||
// wait for engines to stop (ie: providing time for .scriptEnding cleanup handlers to run) before
|
||||
// triggering reload of any Client scripts / Entity scripts
|
||||
QTimer::singleShot(500, this, [=]() {
|
||||
QTimer::singleShot(1000, this, [=]() {
|
||||
for(const auto &scriptName : toReload) {
|
||||
auto scriptEngine = getScriptEngine(scriptName);
|
||||
if (scriptEngine && !scriptEngine->isFinished()) {
|
||||
|
@ -390,6 +402,7 @@ void ScriptEngines::stopAllScripts(bool restart) {
|
|||
qCDebug(scriptengine) << "stopAllScripts -- emitting scriptsReloading";
|
||||
emit scriptsReloading();
|
||||
}
|
||||
_isReloading = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -431,7 +444,6 @@ void ScriptEngines::setScriptsLocation(const QString& scriptsLocation) {
|
|||
void ScriptEngines::reloadAllScripts() {
|
||||
qCDebug(scriptengine) << "reloadAllScripts -- clearing caches";
|
||||
DependencyManager::get<ScriptCache>()->clearCache();
|
||||
DependencyManager::get<OffscreenUi>()->clearCache();
|
||||
qCDebug(scriptengine) << "reloadAllScripts -- stopping all scripts";
|
||||
stopAllScripts(true);
|
||||
}
|
||||
|
@ -533,11 +545,11 @@ void ScriptEngines::launchScriptEngine(ScriptEngine* scriptEngine) {
|
|||
initializer(scriptEngine);
|
||||
}
|
||||
|
||||
// FIXME disabling 'shift key' debugging for now. If you start up the application with
|
||||
// the shift key held down, it triggers a deadlock because of script interfaces running
|
||||
// FIXME disabling 'shift key' debugging for now. If you start up the application with
|
||||
// the shift key held down, it triggers a deadlock because of script interfaces running
|
||||
// on the main thread
|
||||
auto const wantDebug = scriptEngine->isDebuggable(); // || (qApp->queryKeyboardModifiers() & Qt::ShiftModifier);
|
||||
|
||||
|
||||
if (HIFI_SCRIPT_DEBUGGABLES && wantDebug) {
|
||||
scriptEngine->runDebuggable();
|
||||
} else {
|
||||
|
@ -573,5 +585,5 @@ void ScriptEngines::onScriptEngineError(const QString& scriptFilename) {
|
|||
}
|
||||
|
||||
QString ScriptEngines::getDefaultScriptsLocation() const {
|
||||
return defaultScriptsLocation().toString();
|
||||
return PathUtils::defaultScriptsLocation().toString();
|
||||
}
|
||||
|
|
|
@ -66,6 +66,8 @@ public:
|
|||
|
||||
Q_PROPERTY(QString defaultScriptsPath READ getDefaultScriptsLocation)
|
||||
|
||||
void defaultScriptsLocationOverridden(bool overridden) { _defaultScriptsLocationOverridden = overridden; };
|
||||
|
||||
// Called at shutdown time
|
||||
void shutdownScripting();
|
||||
bool isStopped() const { return _isStopped; }
|
||||
|
@ -112,6 +114,8 @@ protected:
|
|||
ScriptsModel _scriptsModel;
|
||||
ScriptsModelFilter _scriptsModelFilter;
|
||||
std::atomic<bool> _isStopped { false };
|
||||
std::atomic<bool> _isReloading { false };
|
||||
bool _defaultScriptsLocationOverridden { false };
|
||||
};
|
||||
|
||||
QUrl normalizeScriptURL(const QUrl& rawScriptURL);
|
||||
|
|
|
@ -125,15 +125,15 @@ int ScriptsModel::columnCount(const QModelIndex& parent) const {
|
|||
|
||||
void ScriptsModel::updateScriptsLocation(const QString& newPath) {
|
||||
_fsWatcher.removePath(_localDirectory.absolutePath());
|
||||
|
||||
|
||||
if (!newPath.isEmpty()) {
|
||||
_localDirectory.setPath(newPath);
|
||||
|
||||
|
||||
if (!_localDirectory.absolutePath().isEmpty()) {
|
||||
_fsWatcher.addPath(_localDirectory.absolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
reloadLocalFiles();
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ void ScriptsModel::reloadDefaultFiles() {
|
|||
}
|
||||
|
||||
void ScriptsModel::requestDefaultFiles(QString marker) {
|
||||
QUrl url(defaultScriptsLocation());
|
||||
QUrl url(PathUtils::defaultScriptsLocation());
|
||||
|
||||
// targets that don't have a scripts folder in the appropriate location will have an empty URL here
|
||||
if (!url.isEmpty()) {
|
||||
|
@ -244,7 +244,7 @@ bool ScriptsModel::parseXML(QByteArray xmlFile) {
|
|||
lastKey = xml.text().toString();
|
||||
if (jsRegex.exactMatch(xml.text().toString())) {
|
||||
QString localPath = lastKey.split("/").mid(1).join("/");
|
||||
QUrl fullPath = defaultScriptsLocation();
|
||||
QUrl fullPath = PathUtils::defaultScriptsLocation();
|
||||
fullPath.setPath(fullPath.path() + lastKey);
|
||||
const QString fullPathStr = normalizeScriptURL(fullPath).toString();
|
||||
_treeNodes.append(new TreeNodeScript(localPath, fullPathStr, SCRIPT_ORIGIN_DEFAULT));
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// CPUDetect.h
|
||||
// libraries/shared/src
|
||||
//
|
||||
// Created by Ken Cooke on 6/6/16.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
// Created by Ken Cooke on 6/16/17.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -13,28 +13,68 @@
|
|||
#define hifi_CPUDetect_h
|
||||
|
||||
//
|
||||
// Lightweight functions to detect SSE/AVX/AVX2 support
|
||||
// Lightweight functions to detect SSE/AVX/AVX2/AVX512 support
|
||||
//
|
||||
|
||||
#define MASK_SSE3 (1 << 0) // SSE3
|
||||
#define MASK_SSSE3 (1 << 9) // SSSE3
|
||||
#define MASK_SSE41 (1 << 19) // SSE4.1
|
||||
#define MASK_SSE42 ((1 << 20) | (1 << 23)) // SSE4.2 and POPCNT
|
||||
#define MASK_OSXSAVE (1 << 27) // OSXSAVE
|
||||
#define MASK_AVX ((1 << 27) | (1 << 28)) // OSXSAVE and AVX
|
||||
#define MASK_AVX2 (1 << 5) // AVX2
|
||||
|
||||
#define MASK_AVX512 ((1 << 16) | (1 << 17) | (1 << 28) | (1 << 30) | (1 << 31)) // AVX512 F,DQ,CD,BW,VL (SKX)
|
||||
|
||||
#define MASK_XCR0_YMM ((1 << 1) | (1 << 2)) // XMM,YMM
|
||||
#define MASK_XCR0_ZMM ((1 << 1) | (1 << 2) | (7 << 5)) // XMM,YMM,ZMM
|
||||
|
||||
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__)
|
||||
#define ARCH_X86
|
||||
#endif
|
||||
|
||||
#define MASK_SSE3 (1 << 0) // SSE3
|
||||
#define MASK_SSSE3 (1 << 9) // SSSE3
|
||||
#define MASK_SSE41 (1 << 19) // SSE4.1
|
||||
#define MASK_SSE42 ((1 << 20) | (1 << 23)) // SSE4.2 and POPCNT
|
||||
#define MASK_AVX ((1 << 27) | (1 << 28)) // OSXSAVE and AVX
|
||||
#define MASK_AVX2 (1 << 5) // AVX2
|
||||
|
||||
#if defined(ARCH_X86) && defined(_MSC_VER)
|
||||
|
||||
#include <intrin.h>
|
||||
|
||||
// use MSVC intrinsics
|
||||
#define cpuidex(info, eax, ecx) __cpuidex(info, eax, ecx)
|
||||
#define xgetbv(ecx) _xgetbv(ecx)
|
||||
|
||||
#elif defined(ARCH_X86) && defined(__GNUC__)
|
||||
|
||||
#include <cpuid.h>
|
||||
|
||||
// use GCC intrinics/asm
|
||||
static inline void cpuidex(int info[4], int eax, int ecx) {
|
||||
__cpuid_count(eax, ecx, info[0], info[1], info[2], info[3]);
|
||||
}
|
||||
|
||||
static inline unsigned long long xgetbv(unsigned int ecx){
|
||||
unsigned int eax, edx;
|
||||
__asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(ecx));
|
||||
return ((unsigned long long)edx << 32) | eax;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void cpuidex(int info[4], int eax, int ecx) {
|
||||
info[0] = 0;
|
||||
info[1] = 0;
|
||||
info[2] = 0;
|
||||
info[3] = 0;
|
||||
}
|
||||
|
||||
static inline unsigned long long xgetbv(unsigned int ecx){
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline bool cpuSupportsSSE3() {
|
||||
int info[4];
|
||||
|
||||
__cpuidex(info, 0x1, 0);
|
||||
cpuidex(info, 0x1, 0);
|
||||
|
||||
return ((info[2] & MASK_SSE3) == MASK_SSE3);
|
||||
}
|
||||
|
@ -42,7 +82,7 @@ static inline bool cpuSupportsSSE3() {
|
|||
static inline bool cpuSupportsSSSE3() {
|
||||
int info[4];
|
||||
|
||||
__cpuidex(info, 0x1, 0);
|
||||
cpuidex(info, 0x1, 0);
|
||||
|
||||
return ((info[2] & MASK_SSSE3) == MASK_SSSE3);
|
||||
}
|
||||
|
@ -50,7 +90,7 @@ static inline bool cpuSupportsSSSE3() {
|
|||
static inline bool cpuSupportsSSE41() {
|
||||
int info[4];
|
||||
|
||||
__cpuidex(info, 0x1, 0);
|
||||
cpuidex(info, 0x1, 0);
|
||||
|
||||
return ((info[2] & MASK_SSE41) == MASK_SSE41);
|
||||
}
|
||||
|
@ -58,7 +98,7 @@ static inline bool cpuSupportsSSE41() {
|
|||
static inline bool cpuSupportsSSE42() {
|
||||
int info[4];
|
||||
|
||||
__cpuidex(info, 0x1, 0);
|
||||
cpuidex(info, 0x1, 0);
|
||||
|
||||
return ((info[2] & MASK_SSE42) == MASK_SSE42);
|
||||
}
|
||||
|
@ -66,13 +106,13 @@ static inline bool cpuSupportsSSE42() {
|
|||
static inline bool cpuSupportsAVX() {
|
||||
int info[4];
|
||||
|
||||
__cpuidex(info, 0x1, 0);
|
||||
cpuidex(info, 0x1, 0);
|
||||
|
||||
bool result = false;
|
||||
if ((info[2] & MASK_AVX) == MASK_AVX) {
|
||||
|
||||
// verify OS support for YMM state
|
||||
if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
|
||||
if ((xgetbv(0) & MASK_XCR0_YMM) == MASK_XCR0_YMM) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +125,7 @@ static inline bool cpuSupportsAVX2() {
|
|||
bool result = false;
|
||||
if (cpuSupportsAVX()) {
|
||||
|
||||
__cpuidex(info, 0x7, 0);
|
||||
cpuidex(info, 0x7, 0);
|
||||
|
||||
if ((info[1] & MASK_AVX2) == MASK_AVX2) {
|
||||
result = true;
|
||||
|
@ -94,62 +134,20 @@ static inline bool cpuSupportsAVX2() {
|
|||
return result;
|
||||
}
|
||||
|
||||
#elif defined(ARCH_X86) && defined(__GNUC__)
|
||||
static inline bool cpuSupportsAVX512() {
|
||||
int info[4];
|
||||
|
||||
#include <cpuid.h>
|
||||
|
||||
static inline bool cpuSupportsSSE3() {
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
|
||||
return __get_cpuid(0x1, &eax, &ebx, &ecx, &edx) && ((ecx & MASK_SSE3) == MASK_SSE3);
|
||||
}
|
||||
|
||||
static inline bool cpuSupportsSSSE3() {
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
|
||||
return __get_cpuid(0x1, &eax, &ebx, &ecx, &edx) && ((ecx & MASK_SSSE3) == MASK_SSSE3);
|
||||
}
|
||||
|
||||
static inline bool cpuSupportsSSE41() {
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
|
||||
return __get_cpuid(0x1, &eax, &ebx, &ecx, &edx) && ((ecx & MASK_SSE41) == MASK_SSE41);
|
||||
}
|
||||
|
||||
static inline bool cpuSupportsSSE42() {
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
|
||||
return __get_cpuid(0x1, &eax, &ebx, &ecx, &edx) && ((ecx & MASK_SSE42) == MASK_SSE42);
|
||||
}
|
||||
|
||||
static inline bool cpuSupportsAVX() {
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
cpuidex(info, 0x1, 0);
|
||||
|
||||
bool result = false;
|
||||
if (__get_cpuid(0x1, &eax, &ebx, &ecx, &edx) && ((ecx & MASK_AVX) == MASK_AVX)) {
|
||||
if ((info[2] & MASK_OSXSAVE) == MASK_OSXSAVE) {
|
||||
|
||||
// verify OS support for YMM state
|
||||
__asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0));
|
||||
if ((eax & 0x6) == 0x6) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// verify OS support for ZMM state
|
||||
if ((xgetbv(0) & MASK_XCR0_ZMM) == MASK_XCR0_ZMM) {
|
||||
|
||||
static inline bool cpuSupportsAVX2() {
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
cpuidex(info, 0x7, 0);
|
||||
|
||||
bool result = false;
|
||||
if (cpuSupportsAVX()) {
|
||||
|
||||
// Work around a bug where __get_cpuid(0x7) returns wrong values on older GCC
|
||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77756
|
||||
if (__get_cpuid(0x0, &eax, &ebx, &ecx, &edx) && (eax >= 0x7)) {
|
||||
|
||||
__cpuid_count(0x7, 0x0, eax, ebx, ecx, edx);
|
||||
|
||||
if ((ebx & MASK_AVX2) == MASK_AVX2) {
|
||||
if ((info[1] & MASK_AVX512) == MASK_AVX512) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
@ -157,32 +155,4 @@ static inline bool cpuSupportsAVX2() {
|
|||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline bool cpuSupportsSSE3() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool cpuSupportsSSSE3() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool cpuSupportsSSE41() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool cpuSupportsSSE42() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool cpuSupportsAVX() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool cpuSupportsAVX2() {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif // hifi_CPUDetect_h
|
||||
|
|
|
@ -71,21 +71,33 @@ QString findMostRecentFileExtension(const QString& originalFileName, QVector<QSt
|
|||
return newestFileName;
|
||||
}
|
||||
|
||||
QUrl defaultScriptsLocation() {
|
||||
// return "http://s3.amazonaws.com/hifi-public";
|
||||
#ifdef Q_OS_WIN
|
||||
QString path = QCoreApplication::applicationDirPath() + "/scripts";
|
||||
#elif defined(Q_OS_OSX)
|
||||
QString path = QCoreApplication::applicationDirPath() + "/../Resources/scripts";
|
||||
#else
|
||||
QString path = QCoreApplication::applicationDirPath() + "/scripts";
|
||||
#endif
|
||||
QUrl PathUtils::defaultScriptsLocation(const QString& newDefaultPath) {
|
||||
static QString overriddenDefaultScriptsLocation = "";
|
||||
QString path;
|
||||
|
||||
// set overriddenDefaultScriptLocation if it was passed in
|
||||
if (!newDefaultPath.isEmpty()) {
|
||||
overriddenDefaultScriptsLocation = newDefaultPath;
|
||||
}
|
||||
|
||||
// use the overridden location if it is set
|
||||
if (!overriddenDefaultScriptsLocation.isEmpty()) {
|
||||
path = overriddenDefaultScriptsLocation;
|
||||
} else {
|
||||
#ifdef Q_OS_WIN
|
||||
path = QCoreApplication::applicationDirPath() + "/scripts";
|
||||
#elif defined(Q_OS_OSX)
|
||||
path = QCoreApplication::applicationDirPath() + "/../Resources/scripts";
|
||||
#else
|
||||
path = QCoreApplication::applicationDirPath() + "/scripts";
|
||||
#endif
|
||||
}
|
||||
|
||||
// turn the string into a legit QUrl
|
||||
QFileInfo fileInfo(path);
|
||||
return QUrl::fromLocalFile(fileInfo.canonicalFilePath());
|
||||
}
|
||||
|
||||
|
||||
QString PathUtils::stripFilename(const QUrl& url) {
|
||||
// Guard against meaningless query and fragment parts.
|
||||
// Do NOT use PreferLocalFile as its behavior is unpredictable (e.g., on defaultScriptsLocation())
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_PathUtils_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include "DependencyManager.h"
|
||||
|
||||
/**jsdoc
|
||||
|
@ -38,11 +37,11 @@ public:
|
|||
static QString stripFilename(const QUrl& url);
|
||||
// note: this is FS-case-sensitive version of parentURL.isParentOf(childURL)
|
||||
static bool isDescendantOf(const QUrl& childURL, const QUrl& parentURL);
|
||||
static QUrl defaultScriptsLocation(const QString& newDefault = "");
|
||||
|
||||
};
|
||||
|
||||
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> possibleExtensions);
|
||||
QString findMostRecentFileExtension(const QString& originalFileName, QVector<QString> possibleExtensions);
|
||||
|
||||
QUrl defaultScriptsLocation();
|
||||
|
||||
#endif // hifi_PathUtils_h
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
set(TARGET_NAME ui)
|
||||
setup_hifi_library(OpenGL Network Qml Quick Script WebChannel WebSockets XmlPatterns)
|
||||
link_hifi_libraries(shared networking gl script-engine)
|
||||
setup_hifi_library(OpenGL Network Qml Quick Script WebChannel WebEngine WebSockets XmlPatterns)
|
||||
link_hifi_libraries(shared networking gl audio)
|
||||
|
||||
if (NOT ANDROID)
|
||||
# Required for some low level GL interaction in the OffscreenQMLSurface
|
||||
target_glew()
|
||||
endif ()
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
#include <AbstractUriHandler.h>
|
||||
#include <AccountManager.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <TabletScriptingInterface.h>
|
||||
|
||||
#include "ui/TabletScriptingInterface.h"
|
||||
#include "FileDialogHelper.h"
|
||||
#include "VrMenu.h"
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
|
||||
#include <gl/OffscreenQmlSurface.h>
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include "ui/OffscreenQmlSurface.h"
|
||||
#include "OffscreenQmlElement.h"
|
||||
|
||||
class VrMenu;
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#include "OffscreenQmlSurface.h"
|
||||
#include "Config.h"
|
||||
|
||||
// Has to come before Qt GL includes
|
||||
#include <gl/Config.h>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
@ -33,14 +35,17 @@
|
|||
#include <NetworkAccessManager.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <shared/GlobalAppProperties.h>
|
||||
#include <FileTypeProfile.h>
|
||||
#include <HFWebEngineProfile.h>
|
||||
#include <HFTabletWebEngineProfile.h>
|
||||
|
||||
#include "OffscreenGLCanvas.h"
|
||||
#include "GLHelpers.h"
|
||||
#include "GLLogging.h"
|
||||
#include "Context.h"
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <gl/Context.h>
|
||||
|
||||
#include "types/FileTypeProfile.h"
|
||||
#include "types/HFWebEngineProfile.h"
|
||||
#include "types/HFTabletWebEngineProfile.h"
|
||||
#include "types/SoundEffect.h"
|
||||
|
||||
#include "Logging.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(trace_render_qml, "trace.render.qml")
|
||||
Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl")
|
||||
|
@ -272,7 +277,7 @@ QString getEventBridgeJavascript() {
|
|||
QString createGlobalEventBridgeStr = QTextStream(&createGlobalEventBridgeFile).readAll();
|
||||
javaScriptToInject = webChannelStr + createGlobalEventBridgeStr;
|
||||
} else {
|
||||
qCWarning(glLogging) << "Unable to find qwebchannel.js or createGlobalEventBridge.js";
|
||||
qCWarning(uiLogging) << "Unable to find qwebchannel.js or createGlobalEventBridge.js";
|
||||
}
|
||||
return javaScriptToInject;
|
||||
}
|
||||
|
@ -297,6 +302,14 @@ private:
|
|||
|
||||
QQmlEngine* acquireEngine(QQuickWindow* window) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
qCWarning(uiLogging) << "Cannot acquire QML engine on any thread but the main thread";
|
||||
}
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [] {
|
||||
qmlRegisterType<SoundEffect>("Hifi", 1, 0, "SoundEffect");
|
||||
});
|
||||
|
||||
if (!globalEngine) {
|
||||
Q_ASSERT(0 == globalEngineRefCount);
|
||||
globalEngine = new QQmlEngine();
|
||||
|
@ -325,8 +338,6 @@ QQmlEngine* acquireEngine(QQuickWindow* window) {
|
|||
rootContext->setContextProperty("FileTypeProfile", new FileTypeProfile(rootContext));
|
||||
rootContext->setContextProperty("HFWebEngineProfile", new HFWebEngineProfile(rootContext));
|
||||
rootContext->setContextProperty("HFTabletWebEngineProfile", new HFTabletWebEngineProfile(rootContext));
|
||||
|
||||
|
||||
}
|
||||
|
||||
++globalEngineRefCount;
|
||||
|
@ -457,7 +468,7 @@ void OffscreenQmlSurface::onAboutToQuit() {
|
|||
}
|
||||
|
||||
void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
||||
qCDebug(glLogging) << "Building QML surface";
|
||||
qCDebug(uiLogging) << "Building QML surface";
|
||||
|
||||
_renderControl = new QMyQuickRenderControl();
|
||||
connect(_renderControl, &QQuickRenderControl::renderRequested, this, [this] { _render = true; });
|
||||
|
@ -548,7 +559,7 @@ void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) {
|
|||
return;
|
||||
}
|
||||
|
||||
qCDebug(glLogging) << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height();
|
||||
qCDebug(uiLogging) << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height();
|
||||
gl::withSavedContext([&] {
|
||||
_canvas->makeCurrent();
|
||||
|
||||
|
@ -595,6 +606,9 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) {
|
|||
}
|
||||
|
||||
QObject* OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function<void(QQmlContext*, QObject*)> f) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qCWarning(uiLogging) << "Called load on a non-surface thread";
|
||||
}
|
||||
// Synchronous loading may take a while; restart the deadlock timer
|
||||
QMetaObject::invokeMethod(qApp, "updateHeartbeat", Qt::DirectConnection);
|
||||
|
||||
|
@ -636,7 +650,7 @@ QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlCon
|
|||
disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0);
|
||||
if (qmlComponent->isError()) {
|
||||
for (const auto& error : qmlComponent->errors()) {
|
||||
qCWarning(glLogging) << error.url() << error.line() << error;
|
||||
qCWarning(uiLogging) << error.url() << error.line() << error;
|
||||
}
|
||||
qmlComponent->deleteLater();
|
||||
return nullptr;
|
||||
|
@ -646,7 +660,7 @@ QObject* OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlCon
|
|||
QObject* newObject = qmlComponent->beginCreate(qmlContext);
|
||||
if (qmlComponent->isError()) {
|
||||
for (const auto& error : qmlComponent->errors()) {
|
||||
qCWarning(glLogging) << error.url() << error.line() << error;
|
||||
qCWarning(uiLogging) << error.url() << error.line() << error;
|
||||
}
|
||||
if (!_rootItem) {
|
||||
qFatal("Unable to finish loading QML root");
|
59
libraries/ui/src/ui/QmlWrapper.cpp
Normal file
59
libraries/ui/src/ui/QmlWrapper.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2017/06/22
|
||||
// Copyright 2013-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 "QmlWrapper.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
QmlWrapper::QmlWrapper(QObject* qmlObject, QObject* parent)
|
||||
: QObject(parent), _qmlObject(qmlObject) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
}
|
||||
|
||||
void QmlWrapper::writeProperty(QString propertyName, QVariant propertyValue) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "writeProperty", Q_ARG(QString, propertyName), Q_ARG(QVariant, propertyValue));
|
||||
}
|
||||
_qmlObject->setProperty(propertyName.toStdString().c_str(), propertyValue);
|
||||
}
|
||||
|
||||
void QmlWrapper::writeProperties(QVariant propertyMap) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "writeProperties", Q_ARG(QVariant, propertyMap));
|
||||
}
|
||||
QVariantMap map = propertyMap.toMap();
|
||||
for (const QString& key : map.keys()) {
|
||||
_qmlObject->setProperty(key.toStdString().c_str(), map[key]);
|
||||
}
|
||||
}
|
||||
|
||||
QVariant QmlWrapper::readProperty(const QString& propertyName) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QVariant result;
|
||||
QMetaObject::invokeMethod(this, "readProperty", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, result), Q_ARG(QString, propertyName));
|
||||
return result;
|
||||
}
|
||||
|
||||
return _qmlObject->property(propertyName.toStdString().c_str());
|
||||
}
|
||||
|
||||
QVariant QmlWrapper::readProperties(const QVariant& propertyList) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QVariant result;
|
||||
QMetaObject::invokeMethod(this, "readProperties", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariant, result), Q_ARG(QVariant, propertyList));
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariantMap result;
|
||||
for (const QVariant& property : propertyList.toList()) {
|
||||
QString propertyString = property.toString();
|
||||
result.insert(propertyString, _qmlObject->property(propertyString.toStdString().c_str()));
|
||||
}
|
||||
return result;
|
||||
}
|
44
libraries/ui/src/ui/QmlWrapper.h
Normal file
44
libraries/ui/src/ui/QmlWrapper.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// Created by Anthony J. Thibault on 2016-12-12
|
||||
// Copyright 2013-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
|
||||
//
|
||||
|
||||
#ifndef hifi_QmlWrapper_h
|
||||
#define hifi_QmlWrapper_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
class QmlWrapper : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QmlWrapper(QObject* qmlObject, QObject* parent = nullptr);
|
||||
|
||||
Q_INVOKABLE void writeProperty(QString propertyName, QVariant propertyValue);
|
||||
Q_INVOKABLE void writeProperties(QVariant propertyMap);
|
||||
Q_INVOKABLE QVariant readProperty(const QString& propertyName);
|
||||
Q_INVOKABLE QVariant readProperties(const QVariant& propertyList);
|
||||
|
||||
protected:
|
||||
QObject* _qmlObject{ nullptr };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
QScriptValue wrapperToScriptValue(QScriptEngine* engine, T* const &in) {
|
||||
if (!in) {
|
||||
return engine->undefinedValue();
|
||||
}
|
||||
return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void wrapperFromScriptValue(const QScriptValue& value, T* &out) {
|
||||
out = qobject_cast<T*>(value.toQObject());
|
||||
}
|
||||
|
||||
#endif
|
|
@ -9,66 +9,51 @@
|
|||
#include "TabletScriptingInterface.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtQml/QQmlProperty>
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include "DependencyManager.h"
|
||||
#include <PathUtils.h>
|
||||
#include <QmlWindowClass.h>
|
||||
#include <QQmlProperty>
|
||||
#include <DependencyManager.h>
|
||||
#include <AccountManager.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include "ScriptEngineLogging.h"
|
||||
#include <OffscreenUi.h>
|
||||
#include <InfoView.h>
|
||||
#include "SoundEffect.h"
|
||||
|
||||
#include "../QmlWindowClass.h"
|
||||
#include "../OffscreenUi.h"
|
||||
#include "../InfoView.h"
|
||||
#include "ToolbarScriptingInterface.h"
|
||||
#include "Logging.h"
|
||||
|
||||
// FIXME move to global app properties
|
||||
const QString SYSTEM_TOOLBAR = "com.highfidelity.interface.toolbar.system";
|
||||
const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
|
||||
|
||||
QScriptValue tabletToScriptValue(QScriptEngine* engine, TabletProxy* const &in) {
|
||||
return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
}
|
||||
|
||||
void tabletFromScriptValue(const QScriptValue& value, TabletProxy* &out) {
|
||||
out = qobject_cast<TabletProxy*>(value.toQObject());
|
||||
}
|
||||
|
||||
TabletScriptingInterface::TabletScriptingInterface() {
|
||||
qmlRegisterType<SoundEffect>("Hifi", 1, 0, "SoundEffect");
|
||||
qCDebug(uiLogging) << "Building tablet scripting interface";
|
||||
}
|
||||
|
||||
QObject* TabletScriptingInterface::getSystemToolbarProxy() {
|
||||
Qt::ConnectionType connectionType = Qt::AutoConnection;
|
||||
if (QThread::currentThread() != _toolbarScriptingInterface->thread()) {
|
||||
connectionType = Qt::BlockingQueuedConnection;
|
||||
}
|
||||
TabletScriptingInterface::~TabletScriptingInterface() {
|
||||
qCDebug(uiLogging) << "Destroying tablet scripting interface";
|
||||
}
|
||||
|
||||
QObject* toolbarProxy = nullptr;
|
||||
bool hasResult = QMetaObject::invokeMethod(_toolbarScriptingInterface, "getToolbar", connectionType, Q_RETURN_ARG(QObject*, toolbarProxy), Q_ARG(QString, SYSTEM_TOOLBAR));
|
||||
if (hasResult) {
|
||||
return toolbarProxy;
|
||||
} else {
|
||||
qCWarning(scriptengine) << "ToolbarScriptingInterface getToolbar has no result";
|
||||
return nullptr;
|
||||
}
|
||||
ToolbarProxy* TabletScriptingInterface::getSystemToolbarProxy() {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
return _toolbarScriptingInterface->getToolbar(SYSTEM_TOOLBAR);
|
||||
}
|
||||
|
||||
TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) {
|
||||
TabletProxy* tabletProxy = nullptr;
|
||||
{
|
||||
// the only thing guarded should be map mutation
|
||||
// this avoids a deadlock with the Main thread
|
||||
// from Qt::BlockingQueuedEvent invocations later in the call-tree
|
||||
std::lock_guard<std::mutex> guard(_mapMutex);
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "getTablet", Qt::BlockingQueuedConnection, Q_RETURN_ARG(TabletProxy*, tabletProxy), Q_ARG(QString, tabletId));
|
||||
return tabletProxy;
|
||||
}
|
||||
|
||||
auto iter = _tabletProxies.find(tabletId);
|
||||
if (iter != _tabletProxies.end()) {
|
||||
// tablet already exists
|
||||
return iter->second;
|
||||
} else {
|
||||
// tablet must be created
|
||||
tabletProxy = new TabletProxy(this, tabletId);
|
||||
_tabletProxies[tabletId] = tabletProxy;
|
||||
}
|
||||
auto iter = _tabletProxies.find(tabletId);
|
||||
if (iter != _tabletProxies.end()) {
|
||||
// tablet already exists
|
||||
return iter->second;
|
||||
} else {
|
||||
// tablet must be created
|
||||
tabletProxy = new TabletProxy(this, tabletId);
|
||||
_tabletProxies[tabletId] = tabletProxy;
|
||||
}
|
||||
|
||||
assert(tabletProxy);
|
||||
|
@ -78,42 +63,40 @@ TabletProxy* TabletScriptingInterface::getTablet(const QString& tabletId) {
|
|||
}
|
||||
|
||||
void TabletScriptingInterface::setToolbarMode(bool toolbarMode) {
|
||||
{
|
||||
// the only thing guarded should be _toolbarMode
|
||||
// this avoids a deadlock with the Main thread
|
||||
// from Qt::BlockingQueuedEvent invocations later in the call-tree
|
||||
std::lock_guard<std::mutex> guard(_mapMutex);
|
||||
_toolbarMode = toolbarMode;
|
||||
}
|
||||
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
_toolbarMode = toolbarMode;
|
||||
for (auto& iter : _tabletProxies) {
|
||||
iter.second->setToolbarMode(toolbarMode);
|
||||
}
|
||||
}
|
||||
|
||||
void TabletScriptingInterface::setQmlTabletRoot(QString tabletId, QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface) {
|
||||
void TabletScriptingInterface::setQmlTabletRoot(QString tabletId, OffscreenQmlSurface* qmlOffscreenSurface) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
TabletProxy* tablet = qobject_cast<TabletProxy*>(getTablet(tabletId));
|
||||
if (tablet) {
|
||||
tablet->setQmlTabletRoot(qmlTabletRoot, qmlOffscreenSurface);
|
||||
tablet->setQmlTabletRoot(qmlOffscreenSurface);
|
||||
} else {
|
||||
qCWarning(scriptengine) << "TabletScriptingInterface::setupTablet() bad tablet object";
|
||||
qCWarning(uiLogging) << "TabletScriptingInterface::setupTablet() bad tablet object";
|
||||
}
|
||||
}
|
||||
|
||||
QQuickWindow* TabletScriptingInterface::getTabletWindow() {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
TabletProxy* tablet = qobject_cast<TabletProxy*>(getTablet(SYSTEM_TABLET));
|
||||
QObject* qmlSurface = tablet->getTabletSurface();
|
||||
|
||||
OffscreenQmlSurface* surface = dynamic_cast<OffscreenQmlSurface*>(qmlSurface);
|
||||
|
||||
if (!surface) {
|
||||
if (!tablet) {
|
||||
return nullptr;
|
||||
}
|
||||
QQuickWindow* window = surface->getWindow();
|
||||
return window;
|
||||
|
||||
auto* qmlSurface = tablet->getTabletSurface();
|
||||
if (!qmlSurface) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return qmlSurface->getWindow();
|
||||
}
|
||||
|
||||
void TabletScriptingInterface::processMenuEvents(QObject* object, const QKeyEvent* event) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Down:
|
||||
QMetaObject::invokeMethod(object, "nextItem");
|
||||
|
@ -141,6 +124,7 @@ void TabletScriptingInterface::processMenuEvents(QObject* object, const QKeyEven
|
|||
}
|
||||
|
||||
void TabletScriptingInterface::processTabletEvents(QObject* object, const QKeyEvent* event) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Down:
|
||||
QMetaObject::invokeMethod(object, "downItem");
|
||||
|
@ -167,8 +151,8 @@ void TabletScriptingInterface::processTabletEvents(QObject* object, const QKeyEv
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void TabletScriptingInterface::processEvent(const QKeyEvent* event) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
TabletProxy* tablet = qobject_cast<TabletProxy*>(getTablet(SYSTEM_TABLET));
|
||||
QObject* qmlTablet = tablet->getQmlTablet();
|
||||
QObject* qmlMenu = tablet->getQmlMenu();
|
||||
|
@ -180,8 +164,8 @@ void TabletScriptingInterface::processEvent(const QKeyEvent* event) {
|
|||
}
|
||||
}
|
||||
|
||||
QObject* TabletScriptingInterface::getFlags()
|
||||
{
|
||||
QObject* TabletScriptingInterface::getFlags() {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->getFlags();
|
||||
}
|
||||
|
@ -198,11 +182,24 @@ class TabletRootWindow : public QmlWindowClass {
|
|||
virtual QString qmlSource() const override { return "hifi/tablet/WindowRoot.qml"; }
|
||||
};
|
||||
|
||||
TabletProxy::TabletProxy(QObject* parent, QString name) : QObject(parent), _name(name) {
|
||||
TabletProxy::TabletProxy(QObject* parent, const QString& name) : QObject(parent), _name(name) {
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
qCWarning(uiLogging) << "Creating tablet proxy on wrong thread " << _name;
|
||||
}
|
||||
}
|
||||
|
||||
TabletProxy::~TabletProxy() {
|
||||
qCDebug(uiLogging) << "Destroying tablet proxy " << _name;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qCWarning(uiLogging) << "Destroying tablet proxy on wrong thread" << _name;
|
||||
}
|
||||
}
|
||||
|
||||
void TabletProxy::setToolbarMode(bool toolbarMode) {
|
||||
std::lock_guard<std::mutex> guard(_tabletMutex);
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setToolbarMode", Q_ARG(bool, toolbarMode));
|
||||
return;
|
||||
}
|
||||
|
||||
if (toolbarMode == _toolbarMode) {
|
||||
return;
|
||||
|
@ -245,25 +242,23 @@ void TabletProxy::setToolbarMode(bool toolbarMode) {
|
|||
}
|
||||
|
||||
static void addButtonProxyToQmlTablet(QQuickItem* qmlTablet, TabletButtonProxy* buttonProxy) {
|
||||
QVariant resultVar;
|
||||
Qt::ConnectionType connectionType = Qt::AutoConnection;
|
||||
if (QThread::currentThread() != qmlTablet->thread()) {
|
||||
connectionType = Qt::BlockingQueuedConnection;
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
if (buttonProxy == NULL){
|
||||
qCCritical(uiLogging) << "TabletScriptingInterface addButtonProxyToQmlTablet buttonProxy is NULL";
|
||||
return;
|
||||
}
|
||||
if (buttonProxy == NULL){
|
||||
qCCritical(scriptengine) << "TabletScriptingInterface addButtonProxyToQmlTablet buttonProxy is NULL";
|
||||
return;
|
||||
}
|
||||
bool hasResult = QMetaObject::invokeMethod(qmlTablet, "addButtonProxy", connectionType,
|
||||
|
||||
QVariant resultVar;
|
||||
bool hasResult = QMetaObject::invokeMethod(qmlTablet, "addButtonProxy", Qt::DirectConnection,
|
||||
Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, buttonProxy->getProperties()));
|
||||
if (!hasResult) {
|
||||
qCWarning(scriptengine) << "TabletScriptingInterface addButtonProxyToQmlTablet has no result";
|
||||
qCWarning(uiLogging) << "TabletScriptingInterface addButtonProxyToQmlTablet has no result";
|
||||
return;
|
||||
}
|
||||
|
||||
QObject* qmlButton = qvariant_cast<QObject *>(resultVar);
|
||||
if (!qmlButton) {
|
||||
qCWarning(scriptengine) << "TabletScriptingInterface addButtonProxyToQmlTablet result not a QObject";
|
||||
qCWarning(uiLogging) << "TabletScriptingInterface addButtonProxyToQmlTablet result not a QObject";
|
||||
return;
|
||||
}
|
||||
QObject::connect(qmlButton, SIGNAL(clicked()), buttonProxy, SLOT(clickedSlot()));
|
||||
|
@ -281,6 +276,11 @@ static QString getUsername() {
|
|||
}
|
||||
|
||||
void TabletProxy::initialScreen(const QVariant& url) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "initialScreen", Q_ARG(QVariant, url));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_qmlTabletRoot) {
|
||||
pushOntoStack(url);
|
||||
} else {
|
||||
|
@ -290,34 +290,49 @@ void TabletProxy::initialScreen(const QVariant& url) {
|
|||
}
|
||||
|
||||
bool TabletProxy::isMessageDialogOpen() {
|
||||
if (_qmlTabletRoot) {
|
||||
QVariant result;
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "isDialogOpen",Qt::DirectConnection,
|
||||
Q_RETURN_ARG(QVariant, result));
|
||||
|
||||
return result.toBool();
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result = false;
|
||||
QMetaObject::invokeMethod(this, "isMessageDialogOpen", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (!_qmlTabletRoot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant result;
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "isDialogOpen",Qt::DirectConnection,
|
||||
Q_RETURN_ARG(QVariant, result));
|
||||
return result.toBool();
|
||||
}
|
||||
|
||||
void TabletProxy::emitWebEvent(QVariant msg) {
|
||||
void TabletProxy::emitWebEvent(const QVariant& msg) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "emitWebEvent", Q_ARG(QVariant, msg));
|
||||
return;
|
||||
}
|
||||
emit webEventReceived(msg);
|
||||
}
|
||||
|
||||
bool TabletProxy::isPathLoaded(QVariant path) {
|
||||
bool TabletProxy::isPathLoaded(const QVariant& path) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result = false;
|
||||
QMetaObject::invokeMethod(this, "isPathLoaded", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path));
|
||||
return result;
|
||||
}
|
||||
|
||||
return path.toString() == _currentPathLoaded.toString();
|
||||
}
|
||||
void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface) {
|
||||
std::lock_guard<std::mutex> guard(_tabletMutex);
|
||||
_qmlOffscreenSurface = qmlOffscreenSurface;
|
||||
_qmlTabletRoot = qmlTabletRoot;
|
||||
if (_qmlTabletRoot && _qmlOffscreenSurface) {
|
||||
|
||||
QObject::connect(_qmlOffscreenSurface, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)), Qt::DirectConnection);
|
||||
void TabletProxy::setQmlTabletRoot(OffscreenQmlSurface* qmlOffscreenSurface) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
_qmlOffscreenSurface = qmlOffscreenSurface;
|
||||
_qmlTabletRoot = qmlOffscreenSurface ? qmlOffscreenSurface->getRootItem() : nullptr;
|
||||
if (_qmlTabletRoot && _qmlOffscreenSurface) {
|
||||
QObject::connect(_qmlOffscreenSurface, SIGNAL(webEventReceived(QVariant)), this, SLOT(emitWebEvent(QVariant)));
|
||||
|
||||
// forward qml surface events to interface js
|
||||
connect(dynamic_cast<OffscreenQmlSurface*>(_qmlOffscreenSurface), &OffscreenQmlSurface::fromQml, [this](QVariant message) {
|
||||
connect(_qmlOffscreenSurface, &OffscreenQmlSurface::fromQml, [this](QVariant message) {
|
||||
if (message.canConvert<QJSValue>()) {
|
||||
emit fromQml(qvariant_cast<QJSValue>(message).toVariant());
|
||||
} else if (message.canConvert<QString>()) {
|
||||
|
@ -330,7 +345,7 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr
|
|||
if (_toolbarMode) {
|
||||
// if someone creates the tablet in toolbar mode, make sure to display the home screen on the tablet.
|
||||
auto loader = _qmlTabletRoot->findChild<QQuickItem*>("loader");
|
||||
QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen()), Qt::DirectConnection);
|
||||
QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen()));
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(TABLET_SOURCE_URL)));
|
||||
}
|
||||
|
||||
|
@ -360,9 +375,18 @@ void TabletProxy::setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscr
|
|||
}
|
||||
|
||||
void TabletProxy::gotoHomeScreen() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "gotoHomeScreen");
|
||||
return;
|
||||
}
|
||||
loadHomeScreen(false);
|
||||
}
|
||||
|
||||
void TabletProxy::gotoMenuScreen(const QString& submenu) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "gotoMenuScreen", Q_ARG(QString, submenu));
|
||||
return;
|
||||
}
|
||||
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
|
@ -385,6 +409,11 @@ void TabletProxy::gotoMenuScreen(const QString& submenu) {
|
|||
}
|
||||
|
||||
void TabletProxy::loadQMLOnTop(const QVariant& path) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "loadQMLOnTop", Q_ARG(QVariant, path));
|
||||
return;
|
||||
}
|
||||
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
root = _qmlTabletRoot;
|
||||
|
@ -396,11 +425,16 @@ void TabletProxy::loadQMLOnTop(const QVariant& path) {
|
|||
QMetaObject::invokeMethod(root, "loadQMLOnTop", Q_ARG(const QVariant&, path));
|
||||
QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));
|
||||
} else {
|
||||
qCDebug(scriptengine) << "tablet cannot load QML because _qmlTabletRoot is null";
|
||||
qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null";
|
||||
}
|
||||
}
|
||||
|
||||
void TabletProxy::returnToPreviousApp() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "returnToPreviousApp");
|
||||
return;
|
||||
}
|
||||
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
root = _qmlTabletRoot;
|
||||
|
@ -412,11 +446,15 @@ void TabletProxy::returnToPreviousApp() {
|
|||
QMetaObject::invokeMethod(root, "returnToPreviousApp");
|
||||
QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));
|
||||
} else {
|
||||
qCDebug(scriptengine) << "tablet cannot load QML because _qmlTabletRoot is null";
|
||||
qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null";
|
||||
}
|
||||
}
|
||||
|
||||
void TabletProxy::loadQMLSource(const QVariant& path) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "loadQMLSource", Q_ARG(QVariant, path));
|
||||
return;
|
||||
}
|
||||
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
|
@ -435,11 +473,17 @@ void TabletProxy::loadQMLSource(const QVariant& path) {
|
|||
QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true)));
|
||||
}
|
||||
} else {
|
||||
qCDebug(scriptengine) << "tablet cannot load QML because _qmlTabletRoot is null";
|
||||
qCDebug(uiLogging) << "tablet cannot load QML because _qmlTabletRoot is null";
|
||||
}
|
||||
}
|
||||
|
||||
bool TabletProxy::pushOntoStack(const QVariant& path) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result = false;
|
||||
QMetaObject::invokeMethod(this, "pushOntoStack", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(QVariant, path));
|
||||
return result;
|
||||
}
|
||||
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
root = _qmlTabletRoot;
|
||||
|
@ -455,13 +499,18 @@ bool TabletProxy::pushOntoStack(const QVariant& path) {
|
|||
loadQMLSource(path);
|
||||
}
|
||||
} else {
|
||||
qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null";
|
||||
qCDebug(uiLogging) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null";
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
void TabletProxy::popFromStack() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "popFromStack");
|
||||
return;
|
||||
}
|
||||
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
root = _qmlTabletRoot;
|
||||
|
@ -473,15 +522,20 @@ void TabletProxy::popFromStack() {
|
|||
auto stack = root->findChild<QQuickItem*>("stack");
|
||||
QMetaObject::invokeMethod(stack, "popSource");
|
||||
} else {
|
||||
qCDebug(scriptengine) << "tablet cannot pop QML because _qmlTabletRoot or _desktopWindow is null";
|
||||
qCDebug(uiLogging) << "tablet cannot pop QML because _qmlTabletRoot or _desktopWindow is null";
|
||||
}
|
||||
}
|
||||
|
||||
void TabletProxy::loadHomeScreen(bool forceOntoHomeScreen) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "loadHomeScreen", Q_ARG(bool, forceOntoHomeScreen));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((_state != State::Home && _state != State::Uninitialized) || forceOntoHomeScreen) {
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
auto loader = _qmlTabletRoot->findChild<QQuickItem*>("loader");
|
||||
QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen()), Qt::DirectConnection);
|
||||
QObject::connect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen()));
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "loadSource", Q_ARG(const QVariant&, QVariant(TABLET_SOURCE_URL)));
|
||||
QMetaObject::invokeMethod(_qmlTabletRoot, "playButtonClickSound");
|
||||
} else if (_toolbarMode && _desktopWindow) {
|
||||
|
@ -505,6 +559,11 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url) {
|
|||
}
|
||||
|
||||
void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJavaScriptUrl) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "loadWebScreenOnTop", Q_ARG(QVariant, url), Q_ARG(QString, injectJavaScriptUrl));
|
||||
return;
|
||||
}
|
||||
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
root = _qmlTabletRoot;
|
||||
|
@ -521,6 +580,10 @@ void TabletProxy::loadWebScreenOnTop(const QVariant& url, const QString& injectJ
|
|||
}
|
||||
|
||||
void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "gotoWebScreen", Q_ARG(QString, url), Q_ARG(QString, injectedJavaScriptUrl));
|
||||
return;
|
||||
}
|
||||
|
||||
QObject* root = nullptr;
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
|
@ -540,57 +603,58 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS
|
|||
_currentPathLoaded = QVariant(url);
|
||||
}
|
||||
|
||||
QObject* TabletProxy::addButton(const QVariant& properties) {
|
||||
TabletButtonProxy* TabletProxy::addButton(const QVariant& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
TabletButtonProxy* result = nullptr;
|
||||
QMetaObject::invokeMethod(this, "addButton", Qt::BlockingQueuedConnection, Q_RETURN_ARG(TabletButtonProxy*, result), Q_ARG(QVariant, properties));
|
||||
return result;
|
||||
}
|
||||
|
||||
auto tabletButtonProxy = QSharedPointer<TabletButtonProxy>(new TabletButtonProxy(properties.toMap()));
|
||||
std::unique_lock<std::mutex> guard(_tabletMutex);
|
||||
_tabletButtonProxies.push_back(tabletButtonProxy);
|
||||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
auto tablet = getQmlTablet();
|
||||
if (tablet) {
|
||||
addButtonProxyToQmlTablet(tablet, tabletButtonProxy.data());
|
||||
} else {
|
||||
qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";
|
||||
qCCritical(uiLogging) << "Could not find tablet in TabletRoot.qml";
|
||||
}
|
||||
} else if (_toolbarMode) {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
|
||||
|
||||
Qt::ConnectionType connectionType = Qt::AutoConnection;
|
||||
if (QThread::currentThread() != toolbarProxy->thread()) {
|
||||
connectionType = Qt::BlockingQueuedConnection;
|
||||
}
|
||||
|
||||
guard.unlock();
|
||||
|
||||
// copy properties from tablet button proxy to toolbar button proxy.
|
||||
QObject* toolbarButtonProxy = nullptr;
|
||||
bool hasResult = QMetaObject::invokeMethod(toolbarProxy, "addButton", connectionType, Q_RETURN_ARG(QObject*, toolbarButtonProxy), Q_ARG(QVariant, tabletButtonProxy->getProperties()));
|
||||
if (hasResult) {
|
||||
tabletButtonProxy->setToolbarButtonProxy(toolbarButtonProxy);
|
||||
} else {
|
||||
qCWarning(scriptengine) << "ToolbarProxy addButton has no result";
|
||||
auto toolbarProxy = DependencyManager::get<TabletScriptingInterface>()->getSystemToolbarProxy();
|
||||
if (toolbarProxy) {
|
||||
// copy properties from tablet button proxy to toolbar button proxy.
|
||||
toolbarProxy->addButton(tabletButtonProxy->getProperties());
|
||||
}
|
||||
}
|
||||
return tabletButtonProxy.data();
|
||||
}
|
||||
|
||||
bool TabletProxy::onHomeScreen() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
bool result = false;
|
||||
QMetaObject::invokeMethod(this, "onHomeScreen", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
return _state == State::Home;
|
||||
}
|
||||
|
||||
void TabletProxy::removeButton(QObject* tabletButtonProxy) {
|
||||
std::unique_lock<std::mutex> guard(_tabletMutex);
|
||||
void TabletProxy::removeButton(TabletButtonProxy* tabletButtonProxy) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "removeButton", Q_ARG(TabletButtonProxy*, tabletButtonProxy));
|
||||
return;
|
||||
}
|
||||
|
||||
auto tablet = getQmlTablet();
|
||||
if (!tablet) {
|
||||
qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml";
|
||||
qCCritical(uiLogging) << "Could not find tablet in TabletRoot.qml";
|
||||
}
|
||||
|
||||
QSharedPointer<TabletButtonProxy> buttonProxy;
|
||||
{
|
||||
auto iter = std::find(_tabletButtonProxies.begin(), _tabletButtonProxies.end(), tabletButtonProxy);
|
||||
if (iter == _tabletButtonProxies.end()) {
|
||||
qCWarning(scriptengine) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy;
|
||||
qCWarning(uiLogging) << "TabletProxy::removeButton() could not find button " << tabletButtonProxy;
|
||||
return;
|
||||
}
|
||||
buttonProxy = *iter;
|
||||
|
@ -600,21 +664,24 @@ void TabletProxy::removeButton(QObject* tabletButtonProxy) {
|
|||
if (!_toolbarMode && _qmlTabletRoot) {
|
||||
buttonProxy->setQmlButton(nullptr);
|
||||
if (tablet) {
|
||||
guard.unlock();
|
||||
QMetaObject::invokeMethod(tablet, "removeButtonProxy", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getProperties()));
|
||||
}
|
||||
} else if (_toolbarMode) {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
|
||||
|
||||
auto toolbarProxy = DependencyManager::get<TabletScriptingInterface>()->getSystemToolbarProxy();
|
||||
// remove button from toolbarProxy
|
||||
guard.unlock();
|
||||
QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getUuid().toString()));
|
||||
buttonProxy->setToolbarButtonProxy(nullptr);
|
||||
if (toolbarProxy) {
|
||||
toolbarProxy->removeButton(buttonProxy->getUuid().toString());
|
||||
buttonProxy->setToolbarButtonProxy(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TabletProxy::emitScriptEvent(QVariant msg) {
|
||||
void TabletProxy::emitScriptEvent(const QVariant& msg) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "emitScriptEvent", Q_ARG(QVariant, msg));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_toolbarMode && _qmlOffscreenSurface) {
|
||||
QMetaObject::invokeMethod(_qmlOffscreenSurface, "emitScriptEvent", Qt::AutoConnection, Q_ARG(QVariant, msg));
|
||||
} else if (_toolbarMode && _desktopWindow) {
|
||||
|
@ -622,7 +689,12 @@ void TabletProxy::emitScriptEvent(QVariant msg) {
|
|||
}
|
||||
}
|
||||
|
||||
void TabletProxy::sendToQml(QVariant msg) {
|
||||
void TabletProxy::sendToQml(const QVariant& msg) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "sendToQml", Q_ARG(QVariant, msg));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_toolbarMode && _qmlOffscreenSurface) {
|
||||
QMetaObject::invokeMethod(_qmlOffscreenSurface, "sendToQml", Qt::AutoConnection, Q_ARG(QVariant, msg));
|
||||
} else if (_toolbarMode && _desktopWindow) {
|
||||
|
@ -635,8 +707,6 @@ void TabletProxy::addButtonsToHomeScreen() {
|
|||
if (!tablet || _toolbarMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
for (auto& buttonProxy : _tabletButtonProxies) {
|
||||
addButtonProxyToQmlTablet(tablet, buttonProxy.data());
|
||||
}
|
||||
|
@ -644,7 +714,7 @@ void TabletProxy::addButtonsToHomeScreen() {
|
|||
QObject::disconnect(loader, SIGNAL(loaded()), this, SLOT(addButtonsToHomeScreen()));
|
||||
}
|
||||
|
||||
QObject* TabletProxy::getTabletSurface() {
|
||||
OffscreenQmlSurface* TabletProxy::getTabletSurface() {
|
||||
return _qmlOffscreenSurface;
|
||||
}
|
||||
|
||||
|
@ -663,35 +733,23 @@ void TabletProxy::desktopWindowClosed() {
|
|||
}
|
||||
|
||||
void TabletProxy::addButtonsToToolbar() {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
|
||||
|
||||
Qt::ConnectionType connectionType = Qt::AutoConnection;
|
||||
if (QThread::currentThread() != toolbarProxy->thread()) {
|
||||
connectionType = Qt::BlockingQueuedConnection;
|
||||
}
|
||||
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
ToolbarProxy* toolbarProxy = DependencyManager::get<TabletScriptingInterface>()->getSystemToolbarProxy();
|
||||
for (auto& buttonProxy : _tabletButtonProxies) {
|
||||
// copy properties from tablet button proxy to toolbar button proxy.
|
||||
QObject* toolbarButtonProxy = nullptr;
|
||||
bool hasResult = QMetaObject::invokeMethod(toolbarProxy, "addButton", connectionType, Q_RETURN_ARG(QObject*, toolbarButtonProxy), Q_ARG(QVariant, buttonProxy->getProperties()));
|
||||
if (hasResult) {
|
||||
buttonProxy->setToolbarButtonProxy(toolbarButtonProxy);
|
||||
} else {
|
||||
qCWarning(scriptengine) << "ToolbarProxy addButton has no result";
|
||||
}
|
||||
buttonProxy->setToolbarButtonProxy(toolbarProxy->addButton(buttonProxy->getProperties()));
|
||||
}
|
||||
|
||||
// make the toolbar visible
|
||||
QMetaObject::invokeMethod(toolbarProxy, "writeProperty", Qt::AutoConnection, Q_ARG(QString, "visible"), Q_ARG(QVariant, QVariant(true)));
|
||||
toolbarProxy->writeProperty("visible", QVariant(true));
|
||||
}
|
||||
|
||||
void TabletProxy::removeButtonsFromToolbar() {
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
QObject* toolbarProxy = tabletScriptingInterface->getSystemToolbarProxy();
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
ToolbarProxy* toolbarProxy = DependencyManager::get<TabletScriptingInterface>()->getSystemToolbarProxy();
|
||||
for (auto& buttonProxy : _tabletButtonProxies) {
|
||||
// remove button from toolbarProxy
|
||||
QMetaObject::invokeMethod(toolbarProxy, "removeButton", Qt::AutoConnection, Q_ARG(QVariant, buttonProxy->getUuid().toString()));
|
||||
toolbarProxy->removeButton(buttonProxy->getUuid().toString());
|
||||
buttonProxy->setToolbarButtonProxy(nullptr);
|
||||
}
|
||||
}
|
||||
|
@ -753,34 +811,56 @@ TabletButtonProxy::TabletButtonProxy(const QVariantMap& properties) :
|
|||
_properties[UUID_KEY] = _uuid;
|
||||
_properties[OBJECT_NAME_KEY] = _uuid.toString();
|
||||
_properties[STABLE_ORDER_KEY] = _stableOrder;
|
||||
if (QThread::currentThread() != qApp->thread()) {
|
||||
qCWarning(uiLogging) << "Creating tablet button proxy on wrong thread";
|
||||
}
|
||||
}
|
||||
|
||||
TabletButtonProxy::~TabletButtonProxy() {
|
||||
qCDebug(uiLogging) << "Destroying tablet button proxy " ;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
qCWarning(uiLogging) << "Destroying tablet button proxy on wrong thread";
|
||||
}
|
||||
}
|
||||
|
||||
void TabletButtonProxy::setQmlButton(QQuickItem* qmlButton) {
|
||||
std::lock_guard<std::mutex> guard(_buttonMutex);
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
_qmlButton = qmlButton;
|
||||
}
|
||||
|
||||
void TabletButtonProxy::setToolbarButtonProxy(QObject* toolbarButtonProxy) {
|
||||
std::lock_guard<std::mutex> guard(_buttonMutex);
|
||||
Q_ASSERT(QThread::currentThread() == thread());
|
||||
_toolbarButtonProxy = toolbarButtonProxy;
|
||||
if (_toolbarButtonProxy) {
|
||||
QObject::connect(_toolbarButtonProxy, SIGNAL(clicked()), this, SLOT(clickedSlot()));
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap TabletButtonProxy::getProperties() const {
|
||||
std::lock_guard<std::mutex> guard(_buttonMutex);
|
||||
QVariantMap TabletButtonProxy::getProperties() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QVariantMap result;
|
||||
QMetaObject::invokeMethod(this, "getProperties", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QVariantMap, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
return _properties;
|
||||
}
|
||||
|
||||
void TabletButtonProxy::editProperties(QVariantMap properties) {
|
||||
std::lock_guard<std::mutex> guard(_buttonMutex);
|
||||
void TabletButtonProxy::editProperties(const QVariantMap& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "editProperties", Q_ARG(QVariantMap, properties));
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap::const_iterator iter = properties.constBegin();
|
||||
while (iter != properties.constEnd()) {
|
||||
_properties[iter.key()] = iter.value();
|
||||
if (_qmlButton) {
|
||||
QMetaObject::invokeMethod(_qmlButton, "changeProperty", Qt::AutoConnection, Q_ARG(QVariant, QVariant(iter.key())), Q_ARG(QVariant, iter.value()));
|
||||
const auto& key = iter.key();
|
||||
const auto& value = iter.value();
|
||||
if (!_properties.contains(key) || _properties[key] != value) {
|
||||
_properties[iter.key()] = iter.value();
|
||||
if (_qmlButton) {
|
||||
QMetaObject::invokeMethod(_qmlButton, "changeProperty", Qt::AutoConnection, Q_ARG(QVariant, QVariant(iter.key())), Q_ARG(QVariant, iter.value()));
|
||||
}
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
|
@ -789,5 +869,3 @@ void TabletButtonProxy::editProperties(QVariantMap properties) {
|
|||
QMetaObject::invokeMethod(_toolbarButtonProxy, "editProperties", Qt::AutoConnection, Q_ARG(QVariantMap, properties));
|
||||
}
|
||||
}
|
||||
|
||||
#include "TabletScriptingInterface.moc"
|
|
@ -26,9 +26,13 @@
|
|||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
class ToolbarProxy;
|
||||
class ToolbarScriptingInterface;
|
||||
|
||||
class TabletProxy;
|
||||
class TabletButtonProxy;
|
||||
class QmlWindowClass;
|
||||
class OffscreenQmlSurface;
|
||||
|
||||
/**jsdoc
|
||||
* @namespace Tablet
|
||||
|
@ -37,9 +41,9 @@ class TabletScriptingInterface : public QObject, public Dependency {
|
|||
Q_OBJECT
|
||||
public:
|
||||
TabletScriptingInterface();
|
||||
~TabletScriptingInterface();
|
||||
|
||||
void setToolbarScriptingInterface(QObject* toolbarScriptingInterface) { _toolbarScriptingInterface = toolbarScriptingInterface; }
|
||||
QObject* getSystemToolbarProxy();
|
||||
void setToolbarScriptingInterface(ToolbarScriptingInterface* toolbarScriptingInterface) { _toolbarScriptingInterface = toolbarScriptingInterface; }
|
||||
|
||||
/**jsdoc
|
||||
* Creates or retruns a new TabletProxy and returns it.
|
||||
|
@ -51,7 +55,7 @@ public:
|
|||
|
||||
void setToolbarMode(bool toolbarMode);
|
||||
|
||||
void setQmlTabletRoot(QString tabletId, QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface);
|
||||
void setQmlTabletRoot(QString tabletId, OffscreenQmlSurface* offscreenQmlSurface);
|
||||
|
||||
void processEvent(const QKeyEvent* event);
|
||||
|
||||
|
@ -67,13 +71,14 @@ signals:
|
|||
void tabletNotification();
|
||||
|
||||
private:
|
||||
friend class TabletProxy;
|
||||
void processMenuEvents(QObject* object, const QKeyEvent* event);
|
||||
void processTabletEvents(QObject* object, const QKeyEvent* event);
|
||||
ToolbarProxy* getSystemToolbarProxy();
|
||||
|
||||
protected:
|
||||
std::mutex _mapMutex;
|
||||
std::map<QString, TabletProxy*> _tabletProxies;
|
||||
QObject* _toolbarScriptingInterface { nullptr };
|
||||
ToolbarScriptingInterface* _toolbarScriptingInterface { nullptr };
|
||||
bool _toolbarMode { false };
|
||||
};
|
||||
|
||||
|
@ -90,19 +95,20 @@ class TabletProxy : public QObject {
|
|||
Q_PROPERTY(bool landscape READ getLandscape WRITE setLandscape)
|
||||
Q_PROPERTY(bool tabletShown MEMBER _tabletShown NOTIFY tabletShownChanged)
|
||||
public:
|
||||
TabletProxy(QObject* parent, QString name);
|
||||
TabletProxy(QObject* parent, const QString& name);
|
||||
~TabletProxy();
|
||||
|
||||
void setQmlTabletRoot(QQuickItem* qmlTabletRoot, QObject* qmlOffscreenSurface);
|
||||
|
||||
Q_INVOKABLE void gotoMenuScreen(const QString& submenu = "");
|
||||
|
||||
QString getName() const { return _name; }
|
||||
void setQmlTabletRoot(OffscreenQmlSurface* offscreenQmlSurface);
|
||||
|
||||
const QString getName() const { return _name; }
|
||||
bool getToolbarMode() const { return _toolbarMode; }
|
||||
void setToolbarMode(bool toolbarMode);
|
||||
|
||||
|
||||
Q_INVOKABLE void gotoMenuScreen(const QString& submenu = "");
|
||||
Q_INVOKABLE void initialScreen(const QVariant& url);
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* transition to the home screen
|
||||
* @function TabletProxy#gotoHomeScreen
|
||||
|
@ -143,28 +149,28 @@ public:
|
|||
* @param properties {Object} button properties UI_TABLET_HACK: enumerate these when we figure out what they should be!
|
||||
* @returns {TabletButtonProxy}
|
||||
*/
|
||||
Q_INVOKABLE QObject* addButton(const QVariant& properties);
|
||||
Q_INVOKABLE TabletButtonProxy* addButton(const QVariant& properties);
|
||||
|
||||
/**jsdoc
|
||||
* removes button from the tablet
|
||||
* @function TabletProxy.removeButton
|
||||
* @param tabletButtonProxy {TabletButtonProxy} button to be removed
|
||||
*/
|
||||
Q_INVOKABLE void removeButton(QObject* tabletButtonProxy);
|
||||
Q_INVOKABLE void removeButton(TabletButtonProxy* tabletButtonProxy);
|
||||
|
||||
/**jsdoc
|
||||
* Used to send an event to the html/js embedded in the tablet
|
||||
* @function TabletProxy#emitScriptEvent
|
||||
* @param msg {object|string}
|
||||
*/
|
||||
Q_INVOKABLE void emitScriptEvent(QVariant msg);
|
||||
Q_INVOKABLE void emitScriptEvent(const QVariant& msg);
|
||||
|
||||
/**jsdoc
|
||||
* Used to send an event to the qml embedded in the tablet
|
||||
* @function TabletProxy#sendToQml
|
||||
* @param msg {object|string}
|
||||
*/
|
||||
Q_INVOKABLE void sendToQml(QVariant msg);
|
||||
Q_INVOKABLE void sendToQml(const QVariant& msg);
|
||||
|
||||
/**jsdoc
|
||||
* Check if the tablet is on the homescreen
|
||||
|
@ -180,11 +186,11 @@ public:
|
|||
Q_INVOKABLE void setLandscape(bool landscape) { _landscape = landscape; }
|
||||
Q_INVOKABLE bool getLandscape() { return _landscape; }
|
||||
|
||||
Q_INVOKABLE bool isPathLoaded(QVariant path);
|
||||
Q_INVOKABLE bool isPathLoaded(const QVariant& path);
|
||||
|
||||
QQuickItem* getTabletRoot() const { return _qmlTabletRoot; }
|
||||
|
||||
QObject* getTabletSurface();
|
||||
OffscreenQmlSurface* getTabletSurface();
|
||||
|
||||
QQuickItem* getQmlTablet() const;
|
||||
|
||||
|
@ -225,7 +231,7 @@ signals:
|
|||
protected slots:
|
||||
void addButtonsToHomeScreen();
|
||||
void desktopWindowClosed();
|
||||
void emitWebEvent(QVariant msg);
|
||||
void emitWebEvent(const QVariant& msg);
|
||||
protected:
|
||||
void removeButtonsFromHomeScreen();
|
||||
void loadHomeScreen(bool forceOntoHomeScreen);
|
||||
|
@ -236,10 +242,9 @@ protected:
|
|||
QVariant _initialPath { "" };
|
||||
QVariant _currentPathLoaded { "" };
|
||||
QString _name;
|
||||
std::mutex _tabletMutex;
|
||||
std::vector<QSharedPointer<TabletButtonProxy>> _tabletButtonProxies;
|
||||
QQuickItem* _qmlTabletRoot { nullptr };
|
||||
QObject* _qmlOffscreenSurface { nullptr };
|
||||
OffscreenQmlSurface* _qmlOffscreenSurface { nullptr };
|
||||
QmlWindowClass* _desktopWindow { nullptr };
|
||||
bool _toolbarMode { false };
|
||||
bool _tabletShown { false };
|
||||
|
@ -251,9 +256,6 @@ protected:
|
|||
|
||||
Q_DECLARE_METATYPE(TabletProxy*);
|
||||
|
||||
QScriptValue tabletToScriptValue(QScriptEngine* engine, TabletProxy* const &in);
|
||||
void tabletFromScriptValue(const QScriptValue& value, TabletProxy* &out);
|
||||
|
||||
/**jsdoc
|
||||
* @class TabletButtonProxy
|
||||
* @property uuid {QUuid} READ_ONLY: uniquely identifies this button
|
||||
|
@ -263,6 +265,7 @@ class TabletButtonProxy : public QObject {
|
|||
Q_PROPERTY(QUuid uuid READ getUuid)
|
||||
public:
|
||||
TabletButtonProxy(const QVariantMap& properties);
|
||||
~TabletButtonProxy();
|
||||
|
||||
void setQmlButton(QQuickItem* qmlButton);
|
||||
void setToolbarButtonProxy(QObject* toolbarButtonProxy);
|
||||
|
@ -274,14 +277,14 @@ public:
|
|||
* @function TabletButtonProxy#getProperties
|
||||
* @returns {ButtonProperties}
|
||||
*/
|
||||
Q_INVOKABLE QVariantMap getProperties() const;
|
||||
Q_INVOKABLE QVariantMap getProperties();
|
||||
|
||||
/**jsdoc
|
||||
* Replace the values of some of this button's properties
|
||||
* @function TabletButtonProxy#editProperties
|
||||
* @param {ButtonProperties} properties - set of properties to change
|
||||
*/
|
||||
Q_INVOKABLE void editProperties(QVariantMap properties);
|
||||
Q_INVOKABLE void editProperties(const QVariantMap& properties);
|
||||
|
||||
public slots:
|
||||
void clickedSlot() { emit clicked(); }
|
||||
|
@ -297,12 +300,13 @@ signals:
|
|||
protected:
|
||||
QUuid _uuid;
|
||||
int _stableOrder;
|
||||
mutable std::mutex _buttonMutex;
|
||||
QQuickItem* _qmlButton { nullptr };
|
||||
QObject* _toolbarButtonProxy { nullptr };
|
||||
QVariantMap _properties;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(TabletButtonProxy*);
|
||||
|
||||
/**jsdoc
|
||||
* @typedef TabletButtonProxy.ButtonProperties
|
||||
* @property {string} icon - url to button icon. (50 x 50)
|
124
libraries/ui/src/ui/ToolbarScriptingInterface.cpp
Normal file
124
libraries/ui/src/ui/ToolbarScriptingInterface.cpp
Normal file
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016-06-16
|
||||
// Copyright 2013-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 "ToolbarScriptingInterface.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
#include <QtQuick/QQuickItem>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include "../OffscreenUi.h"
|
||||
|
||||
QScriptValue toolbarToScriptValue(QScriptEngine* engine, ToolbarProxy* const &in) {
|
||||
if (!in) {
|
||||
return engine->undefinedValue();
|
||||
}
|
||||
return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
}
|
||||
|
||||
void toolbarFromScriptValue(const QScriptValue& value, ToolbarProxy* &out) {
|
||||
out = qobject_cast<ToolbarProxy*>(value.toQObject());
|
||||
}
|
||||
|
||||
QScriptValue toolbarButtonToScriptValue(QScriptEngine* engine, ToolbarButtonProxy* const &in) {
|
||||
if (!in) {
|
||||
return engine->undefinedValue();
|
||||
}
|
||||
return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
}
|
||||
|
||||
void toolbarButtonFromScriptValue(const QScriptValue& value, ToolbarButtonProxy* &out) {
|
||||
out = qobject_cast<ToolbarButtonProxy*>(value.toQObject());
|
||||
}
|
||||
|
||||
|
||||
ToolbarButtonProxy::ToolbarButtonProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
_qmlButton = qobject_cast<QQuickItem*>(qmlObject);
|
||||
connect(qmlObject, SIGNAL(clicked()), this, SIGNAL(clicked()));
|
||||
}
|
||||
|
||||
void ToolbarButtonProxy::editProperties(const QVariantMap& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "editProperties", Q_ARG(QVariantMap, properties));
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantMap::const_iterator iter = properties.constBegin();
|
||||
while (iter != properties.constEnd()) {
|
||||
_properties[iter.key()] = iter.value();
|
||||
if (_qmlButton) {
|
||||
// [01/25 14:26:20] [WARNING] [default] QMetaObject::invokeMethod: No such method ToolbarButton_QMLTYPE_195::changeProperty(QVariant,QVariant)
|
||||
QMetaObject::invokeMethod(_qmlButton, "changeProperty", Qt::AutoConnection,
|
||||
Q_ARG(QVariant, QVariant(iter.key())), Q_ARG(QVariant, iter.value()));
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarProxy::ToolbarProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(qmlObject, parent) {
|
||||
Q_ASSERT(QThread::currentThread() == qApp->thread());
|
||||
}
|
||||
|
||||
ToolbarButtonProxy* ToolbarProxy::addButton(const QVariant& properties) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
ToolbarButtonProxy* result = nullptr;
|
||||
QMetaObject::invokeMethod(this, "addButton", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ToolbarButtonProxy*, result), Q_ARG(QVariant, properties));
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariant resultVar;
|
||||
bool invokeResult = QMetaObject::invokeMethod(_qmlObject, "addButton", Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, properties));
|
||||
if (!invokeResult) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QObject* rawButton = qvariant_cast<QObject *>(resultVar);
|
||||
if (!rawButton) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ToolbarButtonProxy(rawButton, this);
|
||||
}
|
||||
|
||||
void ToolbarProxy::removeButton(const QVariant& name) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "removeButton", Q_ARG(QVariant, name));
|
||||
return;
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(_qmlObject, "removeButton", Q_ARG(QVariant, name));
|
||||
}
|
||||
|
||||
|
||||
ToolbarProxy* ToolbarScriptingInterface::getToolbar(const QString& toolbarId) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
ToolbarProxy* result = nullptr;
|
||||
QMetaObject::invokeMethod(this, "getToolbar", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ToolbarProxy*, result), Q_ARG(QString, toolbarId));
|
||||
return result;
|
||||
}
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto desktop = offscreenUi->getDesktop();
|
||||
Qt::ConnectionType connectionType = Qt::AutoConnection;
|
||||
if (QThread::currentThread() != desktop->thread()) {
|
||||
connectionType = Qt::BlockingQueuedConnection;
|
||||
}
|
||||
QVariant resultVar;
|
||||
bool invokeResult = QMetaObject::invokeMethod(desktop, "getToolbar", connectionType, Q_RETURN_ARG(QVariant, resultVar), Q_ARG(QVariant, toolbarId));
|
||||
if (!invokeResult) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QObject* rawToolbar = qvariant_cast<QObject *>(resultVar);
|
||||
if (!rawToolbar) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ToolbarProxy(rawToolbar);
|
||||
}
|
56
libraries/ui/src/ui/ToolbarScriptingInterface.h
Normal file
56
libraries/ui/src/ui/ToolbarScriptingInterface.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2016-06-16
|
||||
// Copyright 2013-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
|
||||
//
|
||||
|
||||
#ifndef hifi_ToolbarScriptingInterface_h
|
||||
#define hifi_ToolbarScriptingInterface_h
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include "QmlWrapper.h"
|
||||
|
||||
class QQuickItem;
|
||||
|
||||
class ToolbarButtonProxy : public QmlWrapper {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ToolbarButtonProxy(QObject* qmlObject, QObject* parent = nullptr);
|
||||
Q_INVOKABLE void editProperties(const QVariantMap& properties);
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
||||
protected:
|
||||
QQuickItem* _qmlButton { nullptr };
|
||||
QVariantMap _properties;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ToolbarButtonProxy*);
|
||||
|
||||
class ToolbarProxy : public QmlWrapper {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ToolbarProxy(QObject* qmlObject, QObject* parent = nullptr);
|
||||
Q_INVOKABLE ToolbarButtonProxy* addButton(const QVariant& properties);
|
||||
Q_INVOKABLE void removeButton(const QVariant& name);
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(ToolbarProxy*);
|
||||
|
||||
class ToolbarScriptingInterface : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Q_INVOKABLE ToolbarProxy* getToolbar(const QString& toolbarId);
|
||||
};
|
||||
|
||||
|
||||
#endif // hifi_ToolbarScriptingInterface_h
|
|
@ -41,5 +41,3 @@ void SoundEffect::play(QVariant position) {
|
|||
_injector = AudioInjector::playSound(samples, options);
|
||||
}
|
||||
}
|
||||
|
||||
#include "SoundEffect.moc"
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
set(TARGET_NAME hifiCodec)
|
||||
setup_hifi_client_server_plugin()
|
||||
link_hifi_libraries(audio plugins input-plugins display-plugins)
|
||||
link_hifi_libraries(audio plugins)
|
||||
add_dependency_external_projects(hifiAudioCodec)
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${HIFIAUDIOCODEC_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${HIFIAUDIOCODEC_LIBRARIES})
|
||||
|
|
|
@ -10,7 +10,7 @@ if (APPLE OR WIN32)
|
|||
|
||||
set(TARGET_NAME hifiNeuron)
|
||||
setup_hifi_plugin(Script Qml Widgets)
|
||||
link_hifi_libraries(shared controllers ui plugins input-plugins display-plugins)
|
||||
link_hifi_libraries(shared controllers ui plugins input-plugins)
|
||||
target_neuron()
|
||||
|
||||
endif()
|
||||
|
|
|
@ -8,5 +8,5 @@
|
|||
|
||||
set(TARGET_NAME hifiSdl2)
|
||||
setup_hifi_plugin(Script Qml Widgets)
|
||||
link_hifi_libraries(shared controllers ui plugins input-plugins script-engine display-plugins)
|
||||
link_hifi_libraries(shared controllers ui plugins input-plugins script-engine)
|
||||
target_sdl2()
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
if (NOT ANDROID)
|
||||
set(TARGET_NAME hifiSixense)
|
||||
setup_hifi_plugin(Script Qml Widgets)
|
||||
link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins display-plugins)
|
||||
link_hifi_libraries(shared controllers ui plugins ui-plugins input-plugins)
|
||||
target_sixense()
|
||||
endif ()
|
||||
|
|
|
@ -8,6 +8,6 @@
|
|||
|
||||
set(TARGET_NAME pcmCodec)
|
||||
setup_hifi_client_server_plugin()
|
||||
link_hifi_libraries(shared plugins input-plugins display-plugins)
|
||||
link_hifi_libraries(shared plugins)
|
||||
install_beside_console()
|
||||
|
||||
|
|
|
@ -41,7 +41,16 @@ var numStillSnapshotUploadsPending = 0;
|
|||
|
||||
// It's totally unnecessary to return to C++ to perform many of these requests, such as DELETEing an old story,
|
||||
// POSTING a new one, PUTTING a new audience, or GETTING story data. It's far more efficient to do all of that within JS
|
||||
var request = Script.require('request').request;
|
||||
var request;
|
||||
|
||||
try {
|
||||
// Due to an issue where if the user spams 'script reload', this call could cause an exception
|
||||
// preventing our scriptEnding to not properly be initialized resulting in the tablet button
|
||||
// duplicating itself where you end up with a bunch of SNAP buttons on your toolbar
|
||||
request = Script.require('request').request;
|
||||
} catch(err) {
|
||||
print('Failed to resolve request api, error: ' + err);
|
||||
}
|
||||
|
||||
function openLoginWindow() {
|
||||
if ((HMD.active && Settings.getValue("hmdTabletBecomesToolbar", false))
|
||||
|
@ -750,11 +759,10 @@ Script.scriptEnding.connect(function () {
|
|||
}
|
||||
if (tablet) {
|
||||
tablet.removeButton(button);
|
||||
tablet.screenChanged.disconnect(onTabletScreenChanged);
|
||||
}
|
||||
Window.snapshotShared.disconnect(snapshotUploaded);
|
||||
tablet.screenChanged.disconnect(onTabletScreenChanged);
|
||||
Snapshot.snapshotLocationSet.disconnect(snapshotLocationSet);
|
||||
|
||||
Entities.canRezChanged.disconnect(processRezPermissionChange);
|
||||
Entities.canRezTmpChanged.disconnect(processRezPermissionChange);
|
||||
});
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
(function() {
|
||||
Script.include('utils.js');
|
||||
|
||||
var BOOF_SOUND = SoundCache.getSound(Script.resolvePath("sounds/boof.wav"));
|
||||
|
||||
function Enemy() {
|
||||
}
|
||||
Enemy.prototype = {
|
||||
|
@ -38,13 +40,93 @@
|
|||
this.entityIDsThatHaveCollidedWithMe.push(entityB);
|
||||
|
||||
var colliderName = Entities.getEntityProperties(entityB, 'name').name;
|
||||
|
||||
if (colliderName.indexOf("projectile") > -1) {
|
||||
var smokeImage = {
|
||||
"alpha": 0.89999997615814209,
|
||||
"alphaFinish": 0.89999997615814209,
|
||||
"alphaStart": 0.89999997615814209,
|
||||
"azimuthFinish": 0.40000000596046448,
|
||||
"clientOnly": 0,
|
||||
"color": {
|
||||
"blue": 57,
|
||||
"green": 254,
|
||||
"red": 255
|
||||
},
|
||||
"colorFinish": {
|
||||
"blue": 57,
|
||||
"green": 254,
|
||||
"red": 255
|
||||
},
|
||||
"colorSpread": {
|
||||
"blue": 130,
|
||||
"green": 130,
|
||||
"red": 130
|
||||
},
|
||||
"colorStart": {
|
||||
"blue": 57,
|
||||
"green": 254,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-01-10T19:17:07Z",
|
||||
"dimensions": {
|
||||
"x": 1.7600001096725464,
|
||||
"y": 1.7600001096725464,
|
||||
"z": 1.7600001096725464
|
||||
},
|
||||
"emitAcceleration": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"emitOrientation": {
|
||||
"w": 0.7070610523223877,
|
||||
"x": -0.70715254545211792,
|
||||
"y": -1.5258869098033756e-05,
|
||||
"z": -1.5258869098033756e-05
|
||||
},
|
||||
"emitRate": 25.200000762939453,
|
||||
"emitSpeed": 0,
|
||||
"id": "{0273087c-a676-4ac2-93ff-f2440517dfb7}",
|
||||
"lastEdited": 1485295831550663,
|
||||
"lastEditedBy": "{dfe734a0-8289-47f6-a1a6-cf3f6d57adac}",
|
||||
"lifespan": 0.5,
|
||||
"lifetime": 0.5,
|
||||
"locked": 1,
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"particleRadius": 0.30099999904632568,
|
||||
"polarFinish": 3,
|
||||
"position": Entities.getEntityProperties(entityA, 'position').position,
|
||||
"queryAACube": {
|
||||
"scale": 3.0484094619750977,
|
||||
"x": -1.5242047309875488,
|
||||
"y": -1.5242047309875488,
|
||||
"z": -1.5242047309875488
|
||||
},
|
||||
"radiusFinish": 0.30099999904632568,
|
||||
"radiusStart": 0.30099999904632568,
|
||||
"rotation": {
|
||||
"w": 0.086696967482566833,
|
||||
"x": -0.53287714719772339,
|
||||
"y": 0.80860012769699097,
|
||||
"z": -0.23394235968589783
|
||||
},
|
||||
"textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||
"type": "ParticleEffect"
|
||||
};
|
||||
|
||||
Audio.playSound(BOOF_SOUND, {
|
||||
volume: 1.0,
|
||||
position: Entities.getEntityProperties(entityA, 'position').position
|
||||
});
|
||||
|
||||
Entities.addEntity(smokeImage);
|
||||
|
||||
Messages.sendMessage(this.gameChannel, JSON.stringify({
|
||||
type: "enemy-killed",
|
||||
entityID: this.entityID,
|
||||
position: Entities.getEntityProperties(this.entityID, 'position').position
|
||||
}));
|
||||
|
||||
Entities.deleteEntity(this.entityID);
|
||||
} else if (colliderName.indexOf("GateCollider") > -1) {
|
||||
Messages.sendMessage(this.gameChannel, JSON.stringify({
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue