From 516e983dfbd40c5c2b280a38f22c1e71ac98ade1 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 6 Oct 2017 08:03:25 -0700 Subject: [PATCH 01/19] Refactor peakhold to avoid mixed signed/unsigned fixed-point math --- libraries/audio/src/AudioGate.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libraries/audio/src/AudioGate.cpp b/libraries/audio/src/AudioGate.cpp index 13c794b923..30ebbab12a 100644 --- a/libraries/audio/src/AudioGate.cpp +++ b/libraries/audio/src/AudioGate.cpp @@ -100,10 +100,10 @@ protected: int _histogram[NHIST] = {}; // peakhold - int32_t _holdMin = 0x7fffffff; - int32_t _holdInc = 0x7fffffff; + uint32_t _holdMin = 0x7fffffff; + uint32_t _holdInc = 0x7fffffff; uint32_t _holdMax = 0x7fffffff; - int32_t _holdRel = 0x7fffffff; + uint32_t _holdRel = 0x7fffffff; int32_t _holdPeak = 0x7fffffff; // hysteresis @@ -177,7 +177,7 @@ void GateImpl::setThreshold(float threshold) { void GateImpl::setHold(float hold) { const double RELEASE = 100.0; // release = 100ms - const double PROGHOLD = 0.100; // progressive hold = 100ms + const double PROGHOLD = 100.0; // progressive hold = 100ms // pure hold = 1 to 1000ms hold = MAX(hold, 1.0f); @@ -185,10 +185,12 @@ void GateImpl::setHold(float hold) { _holdMin = msToTc(RELEASE, _sampleRate); - _holdInc = (int32_t)((_holdMin - 0x7fffffff) / (PROGHOLD * _sampleRate)); - _holdInc = MIN(_holdInc, -1); // prevent 0 on long releases - - _holdMax = 0x7fffffff - (uint32_t)(_holdInc * (double)hold/1000.0 * _sampleRate); + double progSamples = PROGHOLD/1000.0 * _sampleRate; + _holdInc = (uint32_t)((0x7fffffff - _holdMin) / progSamples); + _holdInc = MAX(_holdInc, 1); // prevent 0 on long releases + + double holdSamples = (double)hold/1000.0 * _sampleRate; + _holdMax = 0x7fffffff + (uint32_t)(_holdInc * holdSamples); } // @@ -336,10 +338,8 @@ int32_t GateImpl::peakhold(int32_t peak) { // (_holdRel > _holdMin) progressive hold // (_holdRel = _holdMin) release - _holdRel += _holdInc; // update progressive hold - _holdRel = MAX((uint32_t)_holdRel, (uint32_t)_holdMin); // saturate at final value - - int32_t tc = MIN((uint32_t)_holdRel, 0x7fffffff); + _holdRel -= _holdInc; // update progressive hold + int32_t tc = MIN(MAX(_holdRel, _holdMin), 0x7fffffff); // saturate to [_holdMin, 0x7fffffff] peak += MULQ31((_holdPeak - peak), tc); // apply release } else { From c6f3a9d9c34dff3d19c83da8b24ac174a1484c5c Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 6 Oct 2017 08:04:18 -0700 Subject: [PATCH 02/19] Better comments --- libraries/audio/src/AudioGate.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/audio/src/AudioGate.cpp b/libraries/audio/src/AudioGate.cpp index 30ebbab12a..deba3f8e58 100644 --- a/libraries/audio/src/AudioGate.cpp +++ b/libraries/audio/src/AudioGate.cpp @@ -183,12 +183,15 @@ void GateImpl::setHold(float hold) { hold = MAX(hold, 1.0f); hold = MIN(hold, 1000.0f); + // compute final tc _holdMin = msToTc(RELEASE, _sampleRate); + // compute tc increment, to progress from 0x7fffffff to _holdMin in PROGHOLD ms double progSamples = PROGHOLD/1000.0 * _sampleRate; _holdInc = (uint32_t)((0x7fffffff - _holdMin) / progSamples); _holdInc = MAX(_holdInc, 1); // prevent 0 on long releases + // compute initial tc, to progress from _holdMax to 0x7fffffff in hold ms double holdSamples = (double)hold/1000.0 * _sampleRate; _holdMax = 0x7fffffff + (uint32_t)(_holdInc * holdSamples); } From 4f193663495d2852d994b6801257f8ea4dcfc7cc Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 6 Oct 2017 08:05:55 -0700 Subject: [PATCH 03/19] Remove debug code --- libraries/audio/src/AudioGate.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/audio/src/AudioGate.cpp b/libraries/audio/src/AudioGate.cpp index deba3f8e58..ac72533806 100644 --- a/libraries/audio/src/AudioGate.cpp +++ b/libraries/audio/src/AudioGate.cpp @@ -323,8 +323,6 @@ void GateImpl::processHistogram(int numFrames) { // smooth threshold update _threshAdapt = threshold + MULQ31((_threshAdapt - threshold), tcThreshold); - - //printf("threshold = %0.1f\n", (_threshAdapt - (LOG2_HEADROOM_Q15 << LOG2_FRACBITS)) * -6.02f / (1 << LOG2_FRACBITS)); } // From b026a0461d77ee3f58b870dcac0ebc8e2ce02082 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 10 Oct 2017 09:13:06 -0700 Subject: [PATCH 04/19] Fix clang warnings for left-shift of signed int --- libraries/audio/src/AudioDynamics.h | 52 ++++++++++++++++------------- libraries/audio/src/AudioGate.cpp | 14 ++++---- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/libraries/audio/src/AudioDynamics.h b/libraries/audio/src/AudioDynamics.h index b7c168bfab..6f36605025 100644 --- a/libraries/audio/src/AudioDynamics.h +++ b/libraries/audio/src/AudioDynamics.h @@ -135,6 +135,7 @@ static const int32_t exp2Table[1 << EXP2_TABBITS][3] = { static const int IEEE754_FABS_MASK = 0x7fffffff; static const int IEEE754_MANT_BITS = 23; +static const int IEEE754_EXPN_BITS = 8; static const int IEEE754_EXPN_BIAS = 127; // @@ -152,7 +153,7 @@ static inline int32_t peaklog2(float* input) { // split into e and x - 1.0 int32_t e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM; - int32_t x = (peak << (31 - IEEE754_MANT_BITS)) & 0x7fffffff; + int32_t x = (peak << IEEE754_EXPN_BITS) & 0x7fffffff; // saturate if (e > 31) { @@ -191,7 +192,7 @@ static inline int32_t peaklog2(float* input0, float* input1) { // split into e and x - 1.0 int32_t e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM; - int32_t x = (peak << (31 - IEEE754_MANT_BITS)) & 0x7fffffff; + int32_t x = (peak << IEEE754_EXPN_BITS) & 0x7fffffff; // saturate if (e > 31) { @@ -234,7 +235,7 @@ static inline int32_t peaklog2(float* input0, float* input1, float* input2, floa // split into e and x - 1.0 int32_t e = IEEE754_EXPN_BIAS - (peak >> IEEE754_MANT_BITS) + LOG2_HEADROOM; - int32_t x = (peak << (31 - IEEE754_MANT_BITS)) & 0x7fffffff; + int32_t x = (peak << IEEE754_EXPN_BITS) & 0x7fffffff; // saturate if (e > 31) { @@ -259,30 +260,30 @@ static inline int32_t peaklog2(float* input0, float* input1, float* input2, floa // Count Leading Zeros // Emulates the CLZ (ARM) and LZCNT (x86) instruction // -static inline int CLZ(uint32_t x) { +static inline int CLZ(uint32_t u) { - if (x == 0) { + if (u == 0) { return 32; } int e = 0; - if (x < 0x00010000) { - x <<= 16; + if (u < 0x00010000) { + u <<= 16; e += 16; } - if (x < 0x01000000) { - x <<= 8; + if (u < 0x01000000) { + u <<= 8; e += 8; } - if (x < 0x10000000) { - x <<= 4; + if (u < 0x10000000) { + u <<= 4; e += 4; } - if (x < 0x40000000) { - x <<= 2; + if (u < 0x40000000) { + u <<= 2; e += 2; } - if (x < 0x80000000) { + if (u < 0x80000000) { e += 1; } return e; @@ -290,19 +291,19 @@ static inline int CLZ(uint32_t x) { // // Compute -log2(x) for x=[0,1] in Q31, result in Q26 -// x = 0 returns 0x7fffffff -// x < 0 undefined +// x <= 0 returns 0x7fffffff // static inline int32_t fixlog2(int32_t x) { - if (x == 0) { + if (x <= 0) { return 0x7fffffff; } // split into e and x - 1.0 - int e = CLZ((uint32_t)x); - x <<= e; // normalize to [0x80000000, 0xffffffff] - x &= 0x7fffffff; // x - 1.0 + uint32_t u = (uint32_t)x; + int e = CLZ(u); + u <<= e; // normalize to [0x80000000, 0xffffffff] + x = u & 0x7fffffff; // x - 1.0 int k = x >> (31 - LOG2_TABBITS); @@ -320,13 +321,18 @@ static inline int32_t fixlog2(int32_t x) { // // Compute exp2(-x) for x=[0,32] in Q26, result in Q31 -// x < 0 undefined +// x <= 0 returns 0x7fffffff // static inline int32_t fixexp2(int32_t x) { + if (x <= 0) { + return 0x7fffffff; + } + // split into e and 1.0 - x - int e = x >> LOG2_FRACBITS; - x = ~(x << LOG2_INTBITS) & 0x7fffffff; + uint32_t u = (uint32_t)x; + int e = u >> LOG2_FRACBITS; + x = ~(u << LOG2_INTBITS) & 0x7fffffff; int k = x >> (31 - EXP2_TABBITS); diff --git a/libraries/audio/src/AudioGate.cpp b/libraries/audio/src/AudioGate.cpp index ac72533806..5ef5ee25b5 100644 --- a/libraries/audio/src/AudioGate.cpp +++ b/libraries/audio/src/AudioGate.cpp @@ -40,7 +40,7 @@ class MonoDCBlock { public: void process(int32_t& x) { - x <<= 15; // scale to Q30 + x *= (1 << 15); // scale to Q30 x -= _dcOffset; // remove DC _dcOffset += x >> 13; // pole = (1.0 - 2^-13) = 0.9999 } @@ -53,8 +53,8 @@ class StereoDCBlock { public: void process(int32_t& x0, int32_t& x1) { - x0 <<= 15; - x1 <<= 15; + x0 *= (1 << 15); + x1 *= (1 << 15); x0 -= _dcOffset[0]; x1 -= _dcOffset[1]; @@ -71,10 +71,10 @@ class QuadDCBlock { public: void process(int32_t& x0, int32_t& x1, int32_t& x2, int32_t& x3) { - x0 <<= 15; - x1 <<= 15; - x2 <<= 15; - x3 <<= 15; + x0 *= (1 << 15); + x1 *= (1 << 15); + x2 *= (1 << 15); + x3 *= (1 << 15); x0 -= _dcOffset[0]; x1 -= _dcOffset[1]; From c31aa6bcdb408e28fba0b2ab151da428ad420ba5 Mon Sep 17 00:00:00 2001 From: beholder Date: Thu, 12 Oct 2017 21:56:59 +0300 Subject: [PATCH 05/19] 8213 Keyboard Input Field: Reveals Passwords --- .../resources/qml/controls-uit/Keyboard.qml | 13 ++++-- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 46 +++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/controls-uit/Keyboard.qml b/interface/resources/qml/controls-uit/Keyboard.qml index 81579d9f71..5b3a69e487 100644 --- a/interface/resources/qml/controls-uit/Keyboard.qml +++ b/interface/resources/qml/controls-uit/Keyboard.qml @@ -13,6 +13,7 @@ import "." Rectangle { id: keyboardBase + objectName: "keyboard" anchors.left: parent.left anchors.right: parent.right @@ -27,6 +28,8 @@ Rectangle { readonly property int mirrorTextHeight: keyboardRowHeight + property bool password: false + property alias mirroredText: mirrorText.text property bool showMirrorText: true readonly property int raisedHeight: 200 @@ -112,16 +115,20 @@ Rectangle { color: "#252525" anchors.horizontalCenter: parent.horizontalCenter - TextEdit { + TextInput { id: mirrorText visible: showMirrorText - size: 13.5 + FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; } + font.family: ralewaySemiBold.name + font.pointSize: 13.5 + verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter color: "#FFFFFF"; anchors.fill: parent wrapMode: Text.WordWrap - readOnly: false // we need to leave this property read-only to allow control to accept QKeyEvent + readOnly: false // we need this to allow control to accept QKeyEvent selectByMouse: false + echoMode: password ? TextInput.Password : TextInput.Normal Keys.onPressed: { if (event.key == Qt.Key_Return) { diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 6cf8a927ff..69d6b8d6ab 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -1030,6 +1030,52 @@ void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool n // if HMD is being worn, allow keyboard to open. allow it to close, HMD or not. if (!raised || qApp->property(hifi::properties::HMD).toBool()) { QQuickItem* item = dynamic_cast(object); + if (!item) + return; + + auto echoMode = item->property("echoMode"); + bool isPasswordField = echoMode.isValid() && echoMode.toInt() == 2 /* TextInput.Password */; + + // we need to somehow pass 'isPasswordField' to visible keyboard so it will change its 'mirror text' to asterixes + // the issue in some cases there might be more than one keyboard in object tree and it is hard to understand which one is being used at the moment + // unfortunately attempts to check for visibility failed becuase visibility is not updated yet. So... I don't see other way than just update properties for all the keyboards + struct Local { + static void forEachKeyboard(QQuickItem* item, std::function function) { + QObject* itemObject = item; + while (itemObject) { + if (itemObject->parent()) { + itemObject = itemObject->parent(); + } + else { + break; + } + } + + auto keyboards = itemObject->findChildren("keyboard"); + + for (auto keyboardObject : keyboards) { + auto keyboard = qobject_cast(keyboardObject); + if (keyboard == nullptr) { + continue; + } + + if (function) { + function(keyboard); + } + } + } + }; + + Local::forEachKeyboard(item, [&](QQuickItem* keyboard) { + keyboard->setProperty("mirroredText", QVariant::fromValue(QString(""))); + keyboard->setProperty("password", isPasswordField); + }); + + // for future probably makes sense to consider one of the following: + // 1. make keyboard a singleton, which will be dynamically re-parented before showing + // 2. track currently visible keyboard somewhere, allow to subscribe for this signal + // any of above should also eliminate need in duplicated properties and code below + while (item) { // Numeric value may be set in parameter from HTML UI; for QML UI, detect numeric fields here. numeric = numeric || QString(item->metaObject()->className()).left(7) == "SpinBox"; From 01c3ca17eeda77ce21f5a00a651bc5cc662c6a43 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 13 Oct 2017 11:05:27 +0200 Subject: [PATCH 06/19] Fixed cube map conversion from equirectangular maps when done after HDR conversion --- libraries/image/src/image/Image.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index 59ec4776a6..5959714cd8 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -983,6 +983,9 @@ public: glm::vec2 dstCoord; glm::ivec2 srcPixel; for (int y = 0; y < faceWidth; ++y) { + QRgb* destScanLineBegin = reinterpret_cast( image.scanLine(y) ); + QRgb* destPixelIterator = destScanLineBegin; + dstCoord.y = 1.0f - (y + 0.5f) * dstInvSize.y; // Fill cube face images from top to bottom for (int x = 0; x < faceWidth; ++x) { dstCoord.x = (x + 0.5f) * dstInvSize.x; @@ -995,13 +998,19 @@ public: srcPixel.y = floor((1.0f - srcCoord.y) * srcFaceHeight); if (((uint32)srcPixel.x < (uint32)source.width()) && ((uint32)srcPixel.y < (uint32)source.height())) { - image.setPixel(x, y, source.pixel(QPoint(srcPixel.x, srcPixel.y))); + // We can't directly use the pixel() method because that launches a pixel color conversion to output + // a correct RGBA8 color. But in our case we may have stored HDR values encoded in a RGB30 format which + // are not convertible by Qt. The same goes with the setPixel method, by the way. + const QRgb* sourcePixelIterator = reinterpret_cast(source.scanLine(srcPixel.y)); + sourcePixelIterator += srcPixel.x; + *destPixelIterator = *sourcePixelIterator; // Keep for debug, this is showing the dir as a color // glm::u8vec4 rgba((xyzDir.x + 1.0)*0.5 * 256, (xyzDir.y + 1.0)*0.5 * 256, (xyzDir.z + 1.0)*0.5 * 256, 256); // unsigned int val = 0xff000000 | (rgba.r) | (rgba.g << 8) | (rgba.b << 16); - // image.setPixel(x, y, val); + // *destPixelIterator = val; } + ++destPixelIterator; } } return image; @@ -1192,6 +1201,10 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& if ((srcImage.width() > 0) && (srcImage.height() > 0)) { QImage image = processSourceImage(srcImage, true); + if (image.format() != QIMAGE_HDR_FORMAT) { + image = convertToHDRFormat(image, HDR_FORMAT); + } + gpu::Element formatMip; gpu::Element formatGPU; if (isCubeTexturesCompressionEnabled()) { @@ -1229,13 +1242,6 @@ gpu::TexturePointer TextureUsage::processCubeTextureColorFromImage(const QImage& faces.push_back(faceImage); } } - - if (image.format() != QIMAGE_HDR_FORMAT) { - for (auto& face : faces) { - face = convertToHDRFormat(face, HDR_FORMAT); - } - } - } else { qCDebug(imagelogging) << "Failed to find a known cube map layout from this image:" << QString(srcImageName.c_str()); return nullptr; From e4e37e65e61d457ab765bdc515b24452e77e4f84 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 13 Oct 2017 08:19:30 -0700 Subject: [PATCH 07/19] Handle packets in-flight during audio codec startup --- libraries/audio/src/InboundAudioStream.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index da8997895c..b7d01c844d 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -161,12 +161,18 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead()); parseAudioData(message.getType(), afterProperties); } else { - qDebug(audio) << "Codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket << "writing silence"; + qDebug(audio) << "Codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket; - // Since the data in the stream is using a codec that we aren't prepared for, - // we need to let the codec know that we don't have data for it, this will - // allow the codec to interpolate missing data and produce a fade to silence. - lostAudioData(1); + if (packetPCM) { + // If there are PCM packets in-flight while the codec is changed, use them. + auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead()); + _ringBuffer.writeData(afterProperties.data(), afterProperties.size()); + } else { + // Since the data in the stream is using a codec that we aren't prepared for, + // we need to let the codec know that we don't have data for it, this will + // allow the codec to interpolate missing data and produce a fade to silence. + lostAudioData(1); + } // inform others of the mismatch auto sendingNode = DependencyManager::get()->nodeWithUUID(message.getSourceID()); From a2a6cd0c7a92339d7da6b302f8d4eb367c456ac2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 13 Oct 2017 14:28:23 -0700 Subject: [PATCH 08/19] Reduce present traffic on main thread event queue. In an effort to diagnose the root cause of several deadlocks, it seems possible that the main thread event queue can grow to a size where it can take many seconds to drain. This PR attempts to address this in two ways: * Change the connection between the DisplayPlugin::presented signal and the Application::onPresent slot to be a DirectConnection. This should prevent the main thread from filling up with signal events. * Within Applicaiton::onPresent use an atomic counter to prevent the main thread from filling up with repeated Idle events. --- interface/src/Application.cpp | 60 ++++++++++++++++++++++++---- interface/src/Application.h | 3 ++ interface/src/Application_render.cpp | 6 +++ 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e5d5e696a1..60b0d536e8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -207,6 +207,17 @@ #if defined(Q_OS_WIN) #include +#ifdef DEBUG_EVENT_QUEUE +// This is a HACK that uses private headers included with the qt source distrubution. +// To use this feature you need to add these directores to your include path: +// E:/Qt/5.9.1/Src/qtbase/include/QtCore/5.9.1/QtCore +// E:/Qt/5.9.1/Src/qtbase/include/QtCore/5.9.1 +#define QT_BOOTSTRAPPED +#include +#include +#undef QT_BOOTSTRAPPED +#endif + extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; } @@ -264,9 +275,7 @@ private: switch ((int)event->type()) { case ApplicationEvent::Render: render(); - // Ensure we never back up the render events. Each render should be triggered only in response - // to the NEXT render event after the last render occured - QCoreApplication::removePostedEvents(this, ApplicationEvent::Render); + qApp->_pendingRenderEventCount--; return true; default: @@ -2712,9 +2721,15 @@ bool Application::importFromZIP(const QString& filePath) { return true; } +// thread-safe void Application::onPresent(quint32 frameCount) { - postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority); - if (_renderEventHandler && !isAboutToQuit()) { + if (_pendingIdleEventCount.load() == 0) { + _pendingIdleEventCount++; + postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority); + } + + if (_renderEventHandler && !isAboutToQuit() && _pendingRenderEventCount.load() == 0) { + _pendingRenderEventCount++; postEvent(_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); } } @@ -2781,7 +2796,26 @@ bool Application::handleFileOpenEvent(QFileOpenEvent* fileEvent) { return false; } +#ifdef DEBUG_EVENT_QUEUE +static int getEventQueueSize(QThread* thread) { + auto threadData = QThreadData::get2(thread); + QMutexLocker locker(&threadData->postEventList.mutex); + return threadData->postEventList.size(); +} + +static void dumpEventQueue(QThread* thread) { + auto threadData = QThreadData::get2(thread); + QMutexLocker locker(&threadData->postEventList.mutex); + qDebug() << "AJT: event list, size =" << threadData->postEventList.size(); + for (auto& postEvent : threadData->postEventList) { + QEvent::Type type = (postEvent.event ? postEvent.event->type() : QEvent::None); + qDebug() << "AJT: " << type; + } +} +#endif // DEBUG_EVENT_QUEUE + bool Application::event(QEvent* event) { + if (!Menu::getInstance()) { return false; } @@ -2801,8 +2835,18 @@ bool Application::event(QEvent* event) { // see (windowMinimizedChanged) case ApplicationEvent::Idle: idle(); - // Don't process extra idle events that arrived in the event queue while we were doing this idle - QCoreApplication::removePostedEvents(this, ApplicationEvent::Idle); + +#ifdef DEBUG_EVENT_QUEUE + { + int count = getEventQueueSize(QThread::currentThread()); + if (count > 400) { + dumpEventQueue(QThread::currentThread()); + } + } +#endif // DEBUG_EVENT_QUEUE + + _pendingIdleEventCount--; + return true; case QEvent::MouseMove: @@ -7203,7 +7247,7 @@ void Application::updateDisplayMode() { _offscreenContext->makeCurrent(); getApplicationCompositor().setDisplayPlugin(newDisplayPlugin); _displayPlugin = newDisplayPlugin; - connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent); + connect(_displayPlugin.get(), &DisplayPlugin::presented, this, &Application::onPresent, Qt::DirectConnection); auto desktop = offscreenUi->getDesktop(); if (desktop) { desktop->setProperty("repositionLocked", wasRepositionLocked); diff --git a/interface/src/Application.h b/interface/src/Application.h index 772646f379..e683adc26b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -717,5 +717,8 @@ private: LaserPointerManager _laserPointerManager; friend class RenderEventHandler; + + std::atomic _pendingIdleEventCount { 0 }; + std::atomic _pendingRenderEventCount { 0 }; }; #endif // hifi_Application_h diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index 541197a660..44d9dfee03 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -72,6 +72,12 @@ void Application::paintGL() { { QMutexLocker viewLocker(&_renderArgsMutex); renderArgs = _appRenderArgs._renderArgs; + + // don't render if there is no context. + if (!_appRenderArgs._renderArgs._context) { + return; + } + HMDSensorPose = _appRenderArgs._headPose; eyeToWorld = _appRenderArgs._eyeToWorld; sensorToWorld = _appRenderArgs._sensorToWorld; From 67d43b4fac11f90ee26342e1b771e8aea3a02b3e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 13 Oct 2017 16:03:56 -0700 Subject: [PATCH 09/19] Use atomic and compare_exchange_strong --- interface/src/Application.cpp | 13 ++++++------- interface/src/Application.h | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 60b0d536e8..f9ad6ec7ff 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -275,7 +275,7 @@ private: switch ((int)event->type()) { case ApplicationEvent::Render: render(); - qApp->_pendingRenderEventCount--; + qApp->_pendingRenderEvent.store(false); return true; default: @@ -2723,13 +2723,12 @@ bool Application::importFromZIP(const QString& filePath) { // thread-safe void Application::onPresent(quint32 frameCount) { - if (_pendingIdleEventCount.load() == 0) { - _pendingIdleEventCount++; + bool expected = false; + if (_pendingIdleEvent.compare_exchange_strong(expected, true)) { postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority); } - - if (_renderEventHandler && !isAboutToQuit() && _pendingRenderEventCount.load() == 0) { - _pendingRenderEventCount++; + expected = false; + if (_renderEventHandler && !isAboutToQuit() && _pendingRenderEvent.compare_exchange_strong(expected, true)) { postEvent(_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render)); } } @@ -2845,7 +2844,7 @@ bool Application::event(QEvent* event) { } #endif // DEBUG_EVENT_QUEUE - _pendingIdleEventCount--; + _pendingIdleEvent.store(false); return true; diff --git a/interface/src/Application.h b/interface/src/Application.h index e683adc26b..b6c09bbd87 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -718,7 +718,7 @@ private: friend class RenderEventHandler; - std::atomic _pendingIdleEventCount { 0 }; - std::atomic _pendingRenderEventCount { 0 }; + std::atomic _pendingIdleEvent { false }; + std::atomic _pendingRenderEvent { false }; }; #endif // hifi_Application_h From f83040c572249d9a823d7b3f50b8f8f043ddda13 Mon Sep 17 00:00:00 2001 From: beholder Date: Fri, 13 Oct 2017 22:23:38 +0300 Subject: [PATCH 10/19] updating code accordingly to the comments --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 60 ++++++++++----------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 69d6b8d6ab..01026ae5ff 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -1018,6 +1018,32 @@ void OffscreenQmlSurface::synthesizeKeyPress(QString key, QObject* targetOverrid } } +static void forEachKeyboard(QQuickItem* item, std::function function) { + QObject* itemObject = item; + while (itemObject) { + if (itemObject->parent()) { + itemObject = itemObject->parent(); + } else { + break; + } + } + + auto keyboards = itemObject->findChildren("keyboard"); + + for (auto keyboardObject : keyboards) { + auto keyboard = qobject_cast(keyboardObject); + if (keyboard == nullptr) { + continue; + } + + if (function) { + function(keyboard); + } + } +} + +static const int TEXTINPUT_PASSWORD = 2; + void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool numeric) { #if Q_OS_ANDROID return; @@ -1030,43 +1056,17 @@ void OffscreenQmlSurface::setKeyboardRaised(QObject* object, bool raised, bool n // if HMD is being worn, allow keyboard to open. allow it to close, HMD or not. if (!raised || qApp->property(hifi::properties::HMD).toBool()) { QQuickItem* item = dynamic_cast(object); - if (!item) + if (!item) { return; + } auto echoMode = item->property("echoMode"); - bool isPasswordField = echoMode.isValid() && echoMode.toInt() == 2 /* TextInput.Password */; + bool isPasswordField = echoMode.isValid() && echoMode.toInt() == TEXTINPUT_PASSWORD; // we need to somehow pass 'isPasswordField' to visible keyboard so it will change its 'mirror text' to asterixes // the issue in some cases there might be more than one keyboard in object tree and it is hard to understand which one is being used at the moment // unfortunately attempts to check for visibility failed becuase visibility is not updated yet. So... I don't see other way than just update properties for all the keyboards - struct Local { - static void forEachKeyboard(QQuickItem* item, std::function function) { - QObject* itemObject = item; - while (itemObject) { - if (itemObject->parent()) { - itemObject = itemObject->parent(); - } - else { - break; - } - } - - auto keyboards = itemObject->findChildren("keyboard"); - - for (auto keyboardObject : keyboards) { - auto keyboard = qobject_cast(keyboardObject); - if (keyboard == nullptr) { - continue; - } - - if (function) { - function(keyboard); - } - } - } - }; - - Local::forEachKeyboard(item, [&](QQuickItem* keyboard) { + forEachKeyboard(item, [&](QQuickItem* keyboard) { keyboard->setProperty("mirroredText", QVariant::fromValue(QString(""))); keyboard->setProperty("password", isPasswordField); }); From a702c5a0e42c50444491fa8a5a68a94323d490e9 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 16 Oct 2017 11:41:40 -0700 Subject: [PATCH 11/19] Fix audio glitches caused by initial burst of SelectedAudioFormat packets. Temporary codec mismatch is expected during audio codec startup, due to packets already in-flight. --- libraries/audio/src/InboundAudioStream.cpp | 31 +++++++++++++++++----- libraries/audio/src/InboundAudioStream.h | 1 + 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index b7d01c844d..8f64cafc7e 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -46,6 +46,10 @@ static const int STATS_FOR_STATS_PACKET_WINDOW_SECONDS = 30; // _currentJitterBufferFrames is updated with the time-weighted avg and the running time-weighted avg is reset. static const quint64 FRAMES_AVAILABLE_STAT_WINDOW_USECS = 10 * USECS_PER_SECOND; +// When the audio codec is switched, temporary codec mismatch is expected due to packets in-flight. +// A SelectedAudioFormat packet is not sent until this threshold is exceeded. +static const int MAX_MISMATCHED_AUDIO_CODEC_COUNT = 10; + InboundAudioStream::InboundAudioStream(int numChannels, int numFrames, int numBlocks, int numStaticJitterBlocks) : _ringBuffer(numChannels * numFrames, numBlocks), _numChannels(numChannels), @@ -134,6 +138,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { switch (arrivalInfo._status) { case SequenceNumberStats::Unreasonable: { lostAudioData(1); + qDebug(audio) << "Sequence Unreasonable (LOST)"; break; } case SequenceNumberStats::Early: { @@ -143,6 +148,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { // fall through to the "on time" logic to actually handle this packet int packetsDropped = arrivalInfo._seqDiffFromExpected; lostAudioData(packetsDropped); + qDebug(audio) << "Sequence Early (LOST)"; // fall through to OnTime case } @@ -153,6 +159,8 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { // If we recieved a SilentAudioFrame from our sender, we might want to drop // some of the samples in order to catch up to our desired jitter buffer size. writeDroppableSilentFrames(networkFrames); + qDebug(audio) << "OnTime (SILENT)"; + } else { // note: PCM and no codec are identical bool selectedPCM = _selectedCodecName == "pcm" || _selectedCodecName == ""; @@ -160,11 +168,15 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { if (codecInPacket == _selectedCodecName || (packetPCM && selectedPCM)) { auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead()); parseAudioData(message.getType(), afterProperties); + qDebug(audio) << "OnTime (DECODE:" << codecInPacket << afterProperties.size() << ")"; + _mismatchedAudioCodecCount = 0; + } else { + _mismatchedAudioCodecCount++; qDebug(audio) << "Codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket; if (packetPCM) { - // If there are PCM packets in-flight while the codec is changed, use them. + // If there are PCM packets in-flight after the codec is changed, use them. auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead()); _ringBuffer.writeData(afterProperties.data(), afterProperties.size()); } else { @@ -174,12 +186,16 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { lostAudioData(1); } - // inform others of the mismatch - auto sendingNode = DependencyManager::get()->nodeWithUUID(message.getSourceID()); - if (sendingNode) { - emit mismatchedAudioCodec(sendingNode, _selectedCodecName, codecInPacket); - } + if (_mismatchedAudioCodecCount > MAX_MISMATCHED_AUDIO_CODEC_COUNT) { + _mismatchedAudioCodecCount = 0; + // inform others of the mismatch + auto sendingNode = DependencyManager::get()->nodeWithUUID(message.getSourceID()); + if (sendingNode) { + emit mismatchedAudioCodec(sendingNode, _selectedCodecName, codecInPacket); + qDebug(audio) << "Codec mismatch threshold exceeded, SelectedAudioFormat(" << _selectedCodecName << " ) sent"; + } + } } } break; @@ -515,6 +531,7 @@ float calculateRepeatedFrameFadeFactor(int indexOfRepeat) { } void InboundAudioStream::setupCodec(CodecPluginPointer codec, const QString& codecName, int numChannels) { + qDebug(audio) << "Setup Codec:" << codecName; cleanupCodec(); // cleanup any previously allocated coders first _codec = codec; _selectedCodecName = codecName; @@ -525,6 +542,8 @@ void InboundAudioStream::setupCodec(CodecPluginPointer codec, const QString& cod void InboundAudioStream::cleanupCodec() { // release any old codec encoder/decoder first... + qDebug(audio) << "Cleanup Codec:" << _selectedCodecName; + if (_codec) { if (_decoder) { _codec->releaseDecoder(_decoder); diff --git a/libraries/audio/src/InboundAudioStream.h b/libraries/audio/src/InboundAudioStream.h index 9494b2f204..ecd1a118f9 100644 --- a/libraries/audio/src/InboundAudioStream.h +++ b/libraries/audio/src/InboundAudioStream.h @@ -186,6 +186,7 @@ protected: CodecPluginPointer _codec; QString _selectedCodecName; Decoder* _decoder { nullptr }; + int _mismatchedAudioCodecCount { 0 }; }; float calculateRepeatedFrameFadeFactor(int indexOfRepeat); From 7c14d5bbdf985cf7ad58b12c866da5a6a2db8538 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 16 Oct 2017 11:43:52 -0700 Subject: [PATCH 12/19] Remove debug logging --- libraries/audio/src/InboundAudioStream.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index 8f64cafc7e..addf572cc3 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -138,7 +138,6 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { switch (arrivalInfo._status) { case SequenceNumberStats::Unreasonable: { lostAudioData(1); - qDebug(audio) << "Sequence Unreasonable (LOST)"; break; } case SequenceNumberStats::Early: { @@ -148,7 +147,6 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { // fall through to the "on time" logic to actually handle this packet int packetsDropped = arrivalInfo._seqDiffFromExpected; lostAudioData(packetsDropped); - qDebug(audio) << "Sequence Early (LOST)"; // fall through to OnTime case } @@ -159,7 +157,6 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { // If we recieved a SilentAudioFrame from our sender, we might want to drop // some of the samples in order to catch up to our desired jitter buffer size. writeDroppableSilentFrames(networkFrames); - qDebug(audio) << "OnTime (SILENT)"; } else { // note: PCM and no codec are identical @@ -168,7 +165,6 @@ int InboundAudioStream::parseData(ReceivedMessage& message) { if (codecInPacket == _selectedCodecName || (packetPCM && selectedPCM)) { auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead()); parseAudioData(message.getType(), afterProperties); - qDebug(audio) << "OnTime (DECODE:" << codecInPacket << afterProperties.size() << ")"; _mismatchedAudioCodecCount = 0; } else { From bd82f47c66dfc67e29201abab1e3838a25a31a7a Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 16 Oct 2017 12:11:50 -0700 Subject: [PATCH 13/19] Remove debug logging --- libraries/audio/src/InboundAudioStream.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index addf572cc3..72acc7fcf6 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -527,7 +527,6 @@ float calculateRepeatedFrameFadeFactor(int indexOfRepeat) { } void InboundAudioStream::setupCodec(CodecPluginPointer codec, const QString& codecName, int numChannels) { - qDebug(audio) << "Setup Codec:" << codecName; cleanupCodec(); // cleanup any previously allocated coders first _codec = codec; _selectedCodecName = codecName; @@ -538,8 +537,6 @@ void InboundAudioStream::setupCodec(CodecPluginPointer codec, const QString& cod void InboundAudioStream::cleanupCodec() { // release any old codec encoder/decoder first... - qDebug(audio) << "Cleanup Codec:" << _selectedCodecName; - if (_codec) { if (_decoder) { _codec->releaseDecoder(_decoder); From 97c255c59ef0eaa6377641c13f250c902f0bc02b Mon Sep 17 00:00:00 2001 From: milad Date: Mon, 16 Oct 2017 16:12:54 -0400 Subject: [PATCH 14/19] Fix for fogbugz:8382 to shutdown debug window on reload scripts --- scripts/developer/debugging/debugWindow.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/developer/debugging/debugWindow.js b/scripts/developer/debugging/debugWindow.js index 6dd116089a..b16739b2b8 100644 --- a/scripts/developer/debugging/debugWindow.js +++ b/scripts/developer/debugging/debugWindow.js @@ -53,4 +53,8 @@ ScriptDiscoveryService.clearDebugWindow.connect(function() { window.clearDebugWindow(); }); +Script.scriptEnding.connect(function () { + window.close(); +}) + }()); From aee817fbbc4c9031066fc2b2f6bba1f9acea047e Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 16 Oct 2017 13:31:25 -0700 Subject: [PATCH 15/19] scale end sphere in c++ for controller modules --- interface/src/raypick/LaserPointer.cpp | 16 +++++++++--- interface/src/raypick/LaserPointer.h | 8 +++++- interface/src/raypick/LaserPointerManager.cpp | 4 +-- interface/src/raypick/LaserPointerManager.h | 2 +- .../LaserPointerScriptingInterface.cpp | 7 +++++- .../controllerModules/farActionGrabEntity.js | 19 +++++--------- .../controllerModules/farTrigger.js | 25 ++++++------------- .../controllerModules/hudOverlayPointer.js | 17 +++---------- .../controllerModules/inEditMode.js | 19 +++----------- .../controllerModules/overlayLaserInput.js | 17 +++---------- .../controllerModules/webEntityLaserInput.js | 15 +++-------- 11 files changed, 58 insertions(+), 91 deletions(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 0e0f13cd6c..0405320423 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -15,13 +15,14 @@ #include "avatar/AvatarManager.h" LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled) : + const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) : _renderingEnabled(enabled), _renderStates(renderStates), _defaultRenderStates(defaultRenderStates), _faceAvatar(faceAvatar), _centerEndY(centerEndY), - _lockEnd(lockEnd) + _lockEnd(lockEnd), + _distanceScaleEnd(distanceScaleEnd) { _rayPickUID = DependencyManager::get()->createRayPick(rayProps); @@ -86,6 +87,10 @@ void LaserPointer::editRenderState(const std::string& state, const QVariant& sta updateRenderStateOverlay(_renderStates[state].getStartID(), startProps); updateRenderStateOverlay(_renderStates[state].getPathID(), pathProps); updateRenderStateOverlay(_renderStates[state].getEndID(), endProps); + QVariant endDim = endProps.toMap()["dimensions"]; + if (endDim.isValid()) { + _renderStates[state].setEndDim(vec3FromVariant(endDim)); + } } void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& props) { @@ -154,10 +159,14 @@ void LaserPointer::updateRenderState(const RenderState& renderState, const Inter if (!renderState.getEndID().isNull()) { QVariantMap endProps; glm::quat faceAvatarRotation = DependencyManager::get()->getMyAvatar()->getOrientation() * glm::quat(glm::radians(glm::vec3(0.0f, 180.0f, 0.0f))); + glm::vec3 dim = vec3FromVariant(qApp->getOverlays().getProperty(renderState.getEndID(), "dimensions").value); + if (_distanceScaleEnd) { + dim = renderState.getEndDim() * glm::distance(pickRay.origin, endVec) * DependencyManager::get()->getMyAvatar()->getSensorToWorldScale(); + endProps.insert("dimensions", vec3toVariant(dim)); + } if (_centerEndY) { endProps.insert("position", end); } else { - glm::vec3 dim = vec3FromVariant(qApp->getOverlays().getProperty(renderState.getEndID(), "dimensions").value); glm::vec3 currentUpVector = faceAvatarRotation * Vectors::UP; endProps.insert("position", vec3toVariant(endVec + glm::vec3(currentUpVector.x * 0.5f * dim.y, currentUpVector.y * 0.5f * dim.y, currentUpVector.z * 0.5f * dim.y))); } @@ -264,6 +273,7 @@ RenderState::RenderState(const OverlayID& startID, const OverlayID& pathID, cons _pathIgnoreRays = qApp->getOverlays().getProperty(_pathID, "ignoreRayIntersection").value.toBool(); } if (!_endID.isNull()) { + _endDim = vec3FromVariant(qApp->getOverlays().getProperty(_endID, "dimensions").value); _endIgnoreRays = qApp->getOverlays().getProperty(_endID, "ignoreRayIntersection").value.toBool(); } } diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 01dfe01cfd..a6b85fbfc2 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -32,6 +32,9 @@ public: const bool& doesPathIgnoreRays() const { return _pathIgnoreRays; } const bool& doesEndIgnoreRays() const { return _endIgnoreRays; } + void setEndDim(const glm::vec3& endDim) { _endDim = endDim; } + const glm::vec3& getEndDim() const { return _endDim; } + void deleteOverlays(); private: @@ -41,6 +44,8 @@ private: bool _startIgnoreRays; bool _pathIgnoreRays; bool _endIgnoreRays; + + glm::vec3 _endDim; }; @@ -52,7 +57,7 @@ public: typedef std::unordered_map> DefaultRenderStateMap; LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled); + const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled); ~LaserPointer(); QUuid getRayUID() { return _rayPickUID; } @@ -88,6 +93,7 @@ private: bool _faceAvatar; bool _centerEndY; bool _lockEnd; + bool _distanceScaleEnd; std::pair _objectLockEnd { std::pair(QUuid(), false)}; QUuid _rayPickUID; diff --git a/interface/src/raypick/LaserPointerManager.cpp b/interface/src/raypick/LaserPointerManager.cpp index 8615a96c3f..7b6e93889d 100644 --- a/interface/src/raypick/LaserPointerManager.cpp +++ b/interface/src/raypick/LaserPointerManager.cpp @@ -11,8 +11,8 @@ #include "LaserPointerManager.h" QUuid LaserPointerManager::createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled) { - std::shared_ptr laserPointer = std::make_shared(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, enabled); + const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) { + std::shared_ptr laserPointer = std::make_shared(rayProps, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled); if (!laserPointer->getRayUID().isNull()) { QWriteLocker containsLock(&_containsLock); QUuid id = QUuid::createUuid(); diff --git a/interface/src/raypick/LaserPointerManager.h b/interface/src/raypick/LaserPointerManager.h index b841877578..29d7be2ed3 100644 --- a/interface/src/raypick/LaserPointerManager.h +++ b/interface/src/raypick/LaserPointerManager.h @@ -22,7 +22,7 @@ class LaserPointerManager { public: QUuid createLaserPointer(const QVariant& rayProps, const LaserPointer::RenderStateMap& renderStates, const LaserPointer::DefaultRenderStateMap& defaultRenderStates, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool enabled); + const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled); void removeLaserPointer(const QUuid uid); void enableLaserPointer(const QUuid uid); void disableLaserPointer(const QUuid uid); diff --git a/interface/src/raypick/LaserPointerScriptingInterface.cpp b/interface/src/raypick/LaserPointerScriptingInterface.cpp index d5e435f490..304a6da4f2 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.cpp +++ b/interface/src/raypick/LaserPointerScriptingInterface.cpp @@ -32,6 +32,11 @@ QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& propert lockEnd = propertyMap["lockEnd"].toBool(); } + bool distanceScaleEnd = false; + if (propertyMap["distanceScaleEnd"].isValid()) { + distanceScaleEnd = propertyMap["distanceScaleEnd"].toBool(); + } + bool enabled = false; if (propertyMap["enabled"].isValid()) { enabled = propertyMap["enabled"].toBool(); @@ -66,7 +71,7 @@ QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& propert } } - return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, enabled); + return qApp->getLaserPointerManager().createLaserPointer(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled); } void LaserPointerScriptingInterface::editRenderState(QUuid uid, const QString& renderState, const QVariant& properties) { diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 2ef7b1efeb..3e980d7f16 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -23,6 +23,8 @@ Script.include("/~/system/libraries/controllers.js"); (function() { var PICK_WITH_HAND_RAY = true; + var SEARCH_SPHERE_SIZE = 0.0132; + var dim = {x: SEARCH_SPHERE_SIZE, y: SEARCH_SPHERE_SIZE, z: SEARCH_SPHERE_SIZE}; var halfPath = { type: "line3d", color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, @@ -37,6 +39,7 @@ Script.include("/~/system/libraries/controllers.js"); }; var halfEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, alpha: 0.9, @@ -58,6 +61,7 @@ Script.include("/~/system/libraries/controllers.js"); }; var fullEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, alpha: 0.9, @@ -135,10 +139,6 @@ Script.include("/~/system/libraries/controllers.js"); 100); this.updateLaserPointer = function(controllerData) { - var SEARCH_SPHERE_SIZE = 0.011; - var MIN_SPHERE_SIZE = 0.0005; - var radius = Math.max(1.2 * SEARCH_SPHERE_SIZE * this.intersectionDistance, MIN_SPHERE_SIZE) * MyAvatar.sensorToWorldScale; - var dim = {x: radius, y: radius, z: radius}; var mode = "hold"; if (!this.distanceHolding && !this.distanceRotating) { if (controllerData.triggerClicks[this.hand]) { @@ -150,16 +150,10 @@ Script.include("/~/system/libraries/controllers.js"); var laserPointerID = PICK_WITH_HAND_RAY ? this.laserPointer : this.headLaserPointer; if (mode === "full") { - var fullEndToEdit = PICK_WITH_HAND_RAY ? this.fullEnd : fullEnd; - fullEndToEdit.dimensions = dim; - LaserPointers.editRenderState(laserPointerID, mode, { path: fullPath, end: fullEndToEdit }); this.contextOverlayTimer = false; this.destroyContextOverlay(); - } else if (mode === "half") { - var halfEndToEdit = PICK_WITH_HAND_RAY ? this.halfEnd : halfEnd; - halfEndToEdit.dimensions = dim; - LaserPointers.editRenderState(laserPointerID, mode, {path: halfPath, end: halfEndToEdit}); } + LaserPointers.enableLaserPointer(laserPointerID); LaserPointers.setRenderState(laserPointerID, mode); if (this.distanceHolding || this.distanceRotating) { @@ -577,8 +571,6 @@ Script.include("/~/system/libraries/controllers.js"); LaserPointers.removeLaserPointer(this.laserPointer); }; - this.halfEnd = halfEnd; - this.fullEnd = fullEnd; this.laserPointer = LaserPointers.createLaserPointer({ joint: (this.hand === RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, @@ -586,6 +578,7 @@ Script.include("/~/system/libraries/controllers.js"); posOffset: getGrabPointSphereOffset(this.handToController(), true), renderStates: renderStates, faceAvatar: true, + distanceScaleEnd: true, defaultRenderStates: defaultRenderStates }); } diff --git a/scripts/system/controllers/controllerModules/farTrigger.js b/scripts/system/controllers/controllerModules/farTrigger.js index a683044e6e..84d4c4d307 100644 --- a/scripts/system/controllers/controllerModules/farTrigger.js +++ b/scripts/system/controllers/controllerModules/farTrigger.js @@ -16,6 +16,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { + var SEARCH_SPHERE_SIZE = 0.0132; + var dim = {x: SEARCH_SPHERE_SIZE, y: SEARCH_SPHERE_SIZE, z: SEARCH_SPHERE_SIZE}; var halfPath = { type: "line3d", color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, @@ -30,6 +32,7 @@ Script.include("/~/system/libraries/controllers.js"); }; var halfEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, alpha: 0.9, @@ -51,6 +54,7 @@ Script.include("/~/system/libraries/controllers.js"); }; var fullEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, alpha: 0.9, @@ -107,10 +111,6 @@ Script.include("/~/system/libraries/controllers.js"); }; this.updateLaserPointer = function(controllerData) { - var SEARCH_SPHERE_SIZE = 0.011; - var MIN_SPHERE_SIZE = 0.0005; - var radius = Math.max(1.2 * SEARCH_SPHERE_SIZE * this.intersectionDistance, MIN_SPHERE_SIZE); - var dim = {x: radius, y: radius, z: radius}; var mode = "none"; if (controllerData.triggerClicks[this.hand]) { mode = "full"; @@ -118,18 +118,8 @@ Script.include("/~/system/libraries/controllers.js"); mode = "half"; } - var laserPointerID = this.laserPointer; - if (mode === "full") { - var fullEndToEdit = this.fullEnd; - fullEndToEdit.dimensions = dim; - LaserPointers.editRenderState(laserPointerID, mode, {path: fullPath, end: fullEndToEdit}); - } else if (mode === "half") { - var halfEndToEdit = this.halfEnd; - halfEndToEdit.dimensions = dim; - LaserPointers.editRenderState(laserPointerID, mode, {path: halfPath, end: halfEndToEdit}); - } - LaserPointers.enableLaserPointer(laserPointerID); - LaserPointers.setRenderState(laserPointerID, mode); + LaserPointers.enableLaserPointer(this.laserPointer); + LaserPointers.setRenderState(this.laserPointer, mode); }; this.laserPointerOff = function() { @@ -192,8 +182,6 @@ Script.include("/~/system/libraries/controllers.js"); return makeRunningValues(true, [this.targetEntityID], []); }; - this.halfEnd = halfEnd; - this.fullEnd = fullEnd; this.laserPointer = LaserPointers.createLaserPointer({ joint: (this.hand === RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, @@ -201,6 +189,7 @@ Script.include("/~/system/libraries/controllers.js"); posOffset: getGrabPointSphereOffset(this.handToController(), true), renderStates: renderStates, faceAvatar: true, + distanceScaleEnd: true, defaultRenderStates: defaultRenderStates }); diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js index fe1dedefb8..9576b3ecc9 100644 --- a/scripts/system/controllers/controllerModules/hudOverlayPointer.js +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -22,6 +22,8 @@ (function() { Script.include("/~/system/libraries/controllers.js"); var ControllerDispatcherUtils = Script.require("/~/system/libraries/controllerDispatcherUtils.js"); + var END_RADIUS = 0.005; + var dim = { x: END_RADIUS, y: END_RADIUS, z: END_RADIUS }; var halfPath = { type: "line3d", color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, @@ -36,6 +38,7 @@ }; var halfEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, alpha: 0.9, @@ -57,6 +60,7 @@ }; var fullEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, alpha: 0.9, @@ -126,17 +130,6 @@ }; this.updateLaserPointer = function(controllerData) { - var RADIUS = 0.005; - var dim = { x: RADIUS, y: RADIUS, z: RADIUS }; - - if (this.mode === "full") { - this.fullEnd.dimensions = dim; - LaserPointers.editRenderState(this.laserPointer, this.mode, {path: fullPath, end: this.fullEnd}); - } else if (this.mode === "half") { - this.halfEnd.dimensions = dim; - LaserPointers.editRenderState(this.laserPointer, this.mode, {path: halfPath, end: this.halfEnd}); - } - LaserPointers.enableLaserPointer(this.laserPointer); LaserPointers.setRenderState(this.laserPointer, this.mode); }; @@ -212,8 +205,6 @@ LaserPointers.removeLaserPointer(this.laserPointer); }; - this.halfEnd = halfEnd; - this.fullEnd = fullEnd; this.laserPointer = LaserPointers.createLaserPointer({ joint: (this.hand === RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_HUD, diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index cbe64b1870..bf96f90a75 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -18,6 +18,8 @@ Script.include("/~/system/libraries/controllers.js"); Script.include("/~/system/libraries/utils.js"); (function () { + var END_RADIUS = 0.005; + var dim = { x: END_RADIUS, y: END_RADIUS, z: END_RADIUS }; var halfPath = { type: "line3d", color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, @@ -32,6 +34,7 @@ Script.include("/~/system/libraries/utils.js"); }; var halfEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, alpha: 0.9, @@ -53,6 +56,7 @@ Script.include("/~/system/libraries/utils.js"); }; var fullEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, alpha: 0.9, @@ -121,17 +125,6 @@ Script.include("/~/system/libraries/utils.js"); }; this.updateLaserPointer = function(controllerData) { - var RADIUS = 0.005; - var dim = { x: RADIUS, y: RADIUS, z: RADIUS }; - - if (this.mode === "full") { - this.fullEnd.dimensions = dim; - LaserPointers.editRenderState(this.laserPointer, this.mode, {path: fullPath, end: this.fullEnd}); - } else if (this.mode === "half") { - this.halfEnd.dimensions = dim; - LaserPointers.editRenderState(this.laserPointer, this.mode, {path: halfPath, end: this.halfEnd}); - } - LaserPointers.enableLaserPointer(this.laserPointer); LaserPointers.setRenderState(this.laserPointer, this.mode); }; @@ -230,10 +223,6 @@ Script.include("/~/system/libraries/utils.js"); LaserPointers.removeLaserPointer(this.laserPointer); }; - - this.halfEnd = halfEnd; - this.fullEnd = fullEnd; - this.laserPointer = LaserPointers.createLaserPointer({ joint: (this.hand === RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, diff --git a/scripts/system/controllers/controllerModules/overlayLaserInput.js b/scripts/system/controllers/controllerModules/overlayLaserInput.js index 7dace85ec4..349d9c0b5c 100644 --- a/scripts/system/controllers/controllerModules/overlayLaserInput.js +++ b/scripts/system/controllers/controllerModules/overlayLaserInput.js @@ -17,6 +17,8 @@ Script.include("/~/system/libraries/controllers.js"); (function() { var TouchEventUtils = Script.require("/~/system/libraries/touchEventUtils.js"); + var END_RADIUS = 0.005; + var dim = { x: END_RADIUS, y: END_RADIUS, z: END_RADIUS }; var halfPath = { type: "line3d", color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, @@ -31,6 +33,7 @@ Script.include("/~/system/libraries/controllers.js"); }; var halfEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, alpha: 0.9, @@ -52,6 +55,7 @@ Script.include("/~/system/libraries/controllers.js"); }; var fullEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, alpha: 0.9, @@ -171,17 +175,6 @@ Script.include("/~/system/libraries/controllers.js"); }; this.updateLaserPointer = function(controllerData) { - var RADIUS = 0.005; - var dim = { x: RADIUS, y: RADIUS, z: RADIUS }; - - if (this.mode === "full") { - this.fullEnd.dimensions = dim; - LaserPointers.editRenderState(this.laserPointer, this.mode, {path: fullPath, end: this.fullEnd}); - } else if (this.mode === "half") { - this.halfEnd.dimensions = dim; - LaserPointers.editRenderState(this.laserPointer, this.mode, {path: halfPath, end: this.halfEnd}); - } - LaserPointers.enableLaserPointer(this.laserPointer); LaserPointers.setRenderState(this.laserPointer, this.mode); }; @@ -366,8 +359,6 @@ Script.include("/~/system/libraries/controllers.js"); LaserPointers.removeLaserPointer(this.laserPointer); }; - this.halfEnd = halfEnd; - this.fullEnd = fullEnd; this.laserPointer = LaserPointers.createLaserPointer({ joint: (this.hand === RIGHT_HAND) ? "_CONTROLLER_RIGHTHAND" : "_CONTROLLER_LEFTHAND", filter: RayPick.PICK_OVERLAYS, diff --git a/scripts/system/controllers/controllerModules/webEntityLaserInput.js b/scripts/system/controllers/controllerModules/webEntityLaserInput.js index 339f248547..62cb05f32f 100644 --- a/scripts/system/controllers/controllerModules/webEntityLaserInput.js +++ b/scripts/system/controllers/controllerModules/webEntityLaserInput.js @@ -17,6 +17,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); (function() { + var END_RADIUS = 0.005; + var dim = { x: END_RADIUS, y: END_RADIUS, z: END_RADIUS }; var halfPath = { type: "line3d", color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, @@ -31,6 +33,7 @@ Script.include("/~/system/libraries/controllers.js"); }; var halfEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, alpha: 0.9, @@ -52,6 +55,7 @@ Script.include("/~/system/libraries/controllers.js"); }; var fullEnd = { type: "sphere", + dimensions: dim, solid: true, color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, alpha: 0.9, @@ -366,17 +370,6 @@ Script.include("/~/system/libraries/controllers.js"); }; this.updateLaserPointer = function(controllerData) { - var RADIUS = 0.005; - var dim = { x: RADIUS, y: RADIUS, z: RADIUS }; - - if (this.mode === "full") { - fullEnd.dimensions = dim; - LaserPointers.editRenderState(this.laserPointer, this.mode, {path: fullPath, end: fullEnd}); - } else if (this.mode === "half") { - halfEnd.dimensions = dim; - LaserPointers.editRenderState(this.laserPointer, this.mode, {path: halfPath, end: halfEnd}); - } - LaserPointers.enableLaserPointer(this.laserPointer); LaserPointers.setRenderState(this.laserPointer, this.mode); }; From 17a68476095ad70b086cba94283fed58125b62cb Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Mon, 16 Oct 2017 14:58:19 -0700 Subject: [PATCH 16/19] Fix CMake command line options --- cmake/macros/AutoScribeShader.cmake | 1 + cmake/macros/SetupQt.cmake | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmake/macros/AutoScribeShader.cmake b/cmake/macros/AutoScribeShader.cmake index c5b35b7e90..1919ecf00a 100755 --- a/cmake/macros/AutoScribeShader.cmake +++ b/cmake/macros/AutoScribeShader.cmake @@ -118,6 +118,7 @@ macro(AUTOSCRIBE_SHADER_LIB) foreach(SHADER_FILE ${SHADER_SOURCE_FILES}) AUTOSCRIBE_SHADER(${SHADER_FILE} ${SHADER_INCLUDE_FILES}) file(TO_CMAKE_PATH "${AUTOSCRIBE_SHADER_RETURN}" AUTOSCRIBE_GENERATED_FILE) + set_property(SOURCE ${AUTOSCRIBE_GENERATED_FILE} PROPERTY SKIP_AUTOMOC ON) list(APPEND AUTOSCRIBE_SHADER_SRC ${AUTOSCRIBE_GENERATED_FILE}) endforeach() #message(${TARGET_NAME} ${AUTOSCRIBE_SHADER_SRC}) diff --git a/cmake/macros/SetupQt.cmake b/cmake/macros/SetupQt.cmake index bbdce06f37..ac67e12044 100644 --- a/cmake/macros/SetupQt.cmake +++ b/cmake/macros/SetupQt.cmake @@ -7,10 +7,12 @@ # function(set_from_env _RESULT_NAME _ENV_VAR_NAME _DEFAULT_VALUE) - if ("$ENV{${_ENV_VAR_NAME}}" STREQUAL "") - set (${_RESULT_NAME} ${_DEFAULT_VALUE} PARENT_SCOPE) - else() - set (${_RESULT_NAME} $ENV{${_ENV_VAR_NAME}} PARENT_SCOPE) + if (NOT DEFINED ${_RESULT_NAME}) + if ("$ENV{${_ENV_VAR_NAME}}" STREQUAL "") + set (${_RESULT_NAME} ${_DEFAULT_VALUE} PARENT_SCOPE) + else() + set (${_RESULT_NAME} $ENV{${_ENV_VAR_NAME}} PARENT_SCOPE) + endif() endif() endfunction() From 1f8bd50d2bcd4d9dc0343d8d0eef9990e5aec6b7 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Mon, 16 Oct 2017 15:19:12 -0700 Subject: [PATCH 17/19] Add clang format specification --- .clang-format | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..f000a27017 --- /dev/null +++ b/.clang-format @@ -0,0 +1,39 @@ +Language: Cpp +Standard: Cpp11 +BasedOnStyle: "Chromium" +ColumnLimit: 128 +IndentWidth: 4 +UseTab: Never + +BreakBeforeBraces: Custom +BraceWrapping: + AfterEnum: true + AfterClass: false + AfterControlStatement: false + AfterFunction: false + AfterNamespace: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + SplitEmptyFunction: false + SplitEmptyNamespace: true + + +AccessModifierOffset: -4 +AllowShortFunctionsOnASingleLine: InlineOnly +BreakConstructorInitializers: BeforeColon +BreakConstructorInitializersBeforeComma: true +IndentCaseLabels: true +ReflowComments: false +Cpp11BracedListStyle: false +ContinuationIndentWidth: 4 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +CompactNamespaces: true +SortIncludes: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements + +PenaltyReturnTypeOnItsOwnLine: 1000 +PenaltyBreakBeforeFirstCallParameter: 1000 + From f53a74b5155849b8ea7ffc1fd6dbcff6698c2521 Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Tue, 17 Oct 2017 11:53:30 -0700 Subject: [PATCH 18/19] Support for custom functionality for specific QML URLs --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 84 ++++++++++++++++++--- libraries/ui/src/ui/OffscreenQmlSurface.h | 18 +++-- 2 files changed, 85 insertions(+), 17 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 6cf8a927ff..bdcbd63c5a 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include "types/FileTypeProfile.h" #include "types/HFWebEngineProfile.h" @@ -52,6 +53,53 @@ Q_LOGGING_CATEGORY(trace_render_qml, "trace.render.qml") Q_LOGGING_CATEGORY(trace_render_qml_gl, "trace.render.qml.gl") Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") + +class OffscreenQmlWhitelist : public Dependency, private ReadWriteLockable { + SINGLETON_DEPENDENCY + +public: + + void addWhitelistContextHandler(const std::initializer_list& urls, const QmlContextCallback& callback) { + withWriteLock([&] { + for (const auto& url : urls) { + _callbacks[url].push_back(callback); + } + }); + } + + QList getCallbacksForUrl(const QUrl& url) const { + return resultWithReadLock>([&] { + QList result; + auto itr = _callbacks.find(url); + if (_callbacks.end() != itr) { + result = *itr; + } + return result; + }); + } + +private: + + QHash> _callbacks; +}; + +QSharedPointer getQmlWhitelist() { + static std::once_flag once; + std::call_once(once, [&] { + DependencyManager::set(); + }); + + return DependencyManager::get(); +} + + +void OffscreenQmlSurface::addWhitelistContextHandler(const std::initializer_list& urls, const QmlContextCallback& callback) { + getQmlWhitelist()->addWhitelistContextHandler(urls, callback); +} + + +QmlContextCallback OffscreenQmlSurface::DEFAULT_CONTEXT_CALLBACK = [](QQmlContext*, QObject*) {}; + struct TextureSet { // The number of surfaces with this size size_t count { 0 }; @@ -640,18 +688,26 @@ void OffscreenQmlSurface::setBaseUrl(const QUrl& baseUrl) { _qmlContext->setBaseUrl(baseUrl); } -void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std::function onQmlLoadedCallback) { +void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, const QmlContextCallback& onQmlLoadedCallback) { 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); + // Get any whitelist functionality + QList callbacks = getQmlWhitelist()->getCallbacksForUrl(qmlSource); + // If we have whitelisted content, we must load a new context + createNewContext |= !callbacks.empty(); + callbacks.push_back(onQmlLoadedCallback); + QQmlContext* targetContext = _qmlContext; if (_rootItem && createNewContext) { targetContext = new QQmlContext(targetContext); } + + // FIXME eliminate loading of relative file paths for QML QUrl finalQmlSource = qmlSource; if ((qmlSource.isRelative() && !qmlSource.isEmpty()) || qmlSource.scheme() == QLatin1String("file")) { finalQmlSource = _qmlContext->resolvedUrl(qmlSource); @@ -659,29 +715,32 @@ void OffscreenQmlSurface::load(const QUrl& qmlSource, bool createNewContext, std auto qmlComponent = new QQmlComponent(_qmlContext->engine(), finalQmlSource, QQmlComponent::PreferSynchronous); if (qmlComponent->isLoading()) { - connect(qmlComponent, &QQmlComponent::statusChanged, this, - [this, qmlComponent, targetContext, onQmlLoadedCallback](QQmlComponent::Status) { - finishQmlLoad(qmlComponent, targetContext, onQmlLoadedCallback); + connect(qmlComponent, &QQmlComponent::statusChanged, this, [=](QQmlComponent::Status) { + finishQmlLoad(qmlComponent, targetContext, callbacks); }); return; } - finishQmlLoad(qmlComponent, targetContext, onQmlLoadedCallback); + finishQmlLoad(qmlComponent, targetContext, callbacks); } -void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, std::function onQmlLoadedCallback) { +void OffscreenQmlSurface::loadInNewContext(const QUrl& qmlSource, const QmlContextCallback& onQmlLoadedCallback) { load(qmlSource, true, onQmlLoadedCallback); } -void OffscreenQmlSurface::load(const QUrl& qmlSource, std::function onQmlLoadedCallback) { +void OffscreenQmlSurface::load(const QUrl& qmlSource, const QmlContextCallback& onQmlLoadedCallback) { load(qmlSource, false, onQmlLoadedCallback); } +void OffscreenQmlSurface::load(const QString& qmlSourceFile, const QmlContextCallback& onQmlLoadedCallback) { + return load(QUrl(qmlSourceFile), onQmlLoadedCallback); +} + void OffscreenQmlSurface::clearCache() { _qmlContext->engine()->clearComponentCache(); } -void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function onQmlLoadedCallback) { +void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, const QList& callbacks) { disconnect(qmlComponent, &QQmlComponent::statusChanged, this, 0); if (qmlComponent->isError()) { for (const auto& error : qmlComponent->errors()) { @@ -716,7 +775,9 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext // Make sure we will call callback for this codepath // Call this before qmlComponent->completeCreate() otherwise ghost window appears if (newItem && _rootItem) { - onQmlLoadedCallback(qmlContext, newObject); + for (const auto& callback : callbacks) { + callback(qmlContext, newObject); + } } QObject* eventBridge = qmlContext->contextProperty("eventBridge").value(); @@ -751,8 +812,11 @@ void OffscreenQmlSurface::finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext _rootItem = newItem; _rootItem->setParentItem(_quickWindow->contentItem()); _rootItem->setSize(_quickWindow->renderTargetSize()); + // Call this callback after rootitem is set, otherwise VrMenu wont work - onQmlLoadedCallback(qmlContext, newObject); + for (const auto& callback : callbacks) { + callback(qmlContext, newObject); + } } void OffscreenQmlSurface::updateQuick() { diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 990f81848d..74eafe9f13 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -35,12 +35,18 @@ class QQuickItem; // one copy in flight, and one copy being used by the receiver #define GPU_RESOURCE_BUFFER_SIZE 3 +using QmlContextCallback = std::function; + class OffscreenQmlSurface : public QObject { Q_OBJECT Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged) public: static void setSharedContext(QOpenGLContext* context); + static QmlContextCallback DEFAULT_CONTEXT_CALLBACK; + static void addWhitelistContextHandler(const std::initializer_list& urls, const QmlContextCallback& callback); + static void addWhitelistContextHandler(const QUrl& url, const QmlContextCallback& callback) { addWhitelistContextHandler({ { url } }, callback); }; + OffscreenQmlSurface(); virtual ~OffscreenQmlSurface(); @@ -50,12 +56,10 @@ public: void resize(const QSize& size, bool forceResize = false); QSize size() const; - Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {}); - Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {}); - Q_INVOKABLE void load(const QUrl& qmlSource, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {}); - Q_INVOKABLE void load(const QString& qmlSourceFile, std::function onQmlLoadedCallback = [](QQmlContext*, QObject*) {}) { - return load(QUrl(qmlSourceFile), onQmlLoadedCallback); - } + Q_INVOKABLE void load(const QUrl& qmlSource, bool createNewContext, const QmlContextCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK); + Q_INVOKABLE void loadInNewContext(const QUrl& qmlSource, const QmlContextCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK); + Q_INVOKABLE void load(const QUrl& qmlSource, const QmlContextCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK); + Q_INVOKABLE void load(const QString& qmlSourceFile, const QmlContextCallback& onQmlLoadedCallback = DEFAULT_CONTEXT_CALLBACK); void clearCache(); void setMaxFps(uint8_t maxFps) { _maxFps = maxFps; } // Optional values for event handling @@ -120,7 +124,7 @@ protected: private: static QOpenGLContext* getSharedContext(); - void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, std::function onQmlLoadedCallback); + void finishQmlLoad(QQmlComponent* qmlComponent, QQmlContext* qmlContext, const QList& callbacks); QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject); void setupFbo(); bool allowNewFrame(uint8_t fps); From 9a881c5e57e4267f5d6d6b334bb22e9be73c4fc2 Mon Sep 17 00:00:00 2001 From: druiz17 Date: Tue, 17 Oct 2017 15:43:23 -0700 Subject: [PATCH 19/19] fix keyboard --- interface/resources/html/raiseAndLowerKeyboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/html/raiseAndLowerKeyboard.js b/interface/resources/html/raiseAndLowerKeyboard.js index ad1d889556..2535416fd8 100644 --- a/interface/resources/html/raiseAndLowerKeyboard.js +++ b/interface/resources/html/raiseAndLowerKeyboard.js @@ -18,7 +18,7 @@ function shouldRaiseKeyboard() { var nodeName = document.activeElement.nodeName; var nodeType = document.activeElement.type; - if (nodeName === "INPUT" && ["email", "number", "password", "tel", "text", "url"].indexOf(nodeType) !== -1 + if (nodeName === "INPUT" && ["email", "number", "password", "tel", "text", "url", "search"].indexOf(nodeType) !== -1 || document.activeElement.nodeName === "TEXTAREA") { return true; } else {