Merge branch 'master' into hazeZone

This commit is contained in:
Nissim 2017-10-17 09:28:07 -07:00
commit afa93f46e5
9 changed files with 164 additions and 30 deletions

View file

@ -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 || event.key == Qt.Key_Space) {

View file

@ -207,6 +207,17 @@
#if defined(Q_OS_WIN)
#include <VersionHelpers.h>
#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 <private/qthread_p.h>
#include <private/qobject_p.h>
#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->_pendingRenderEvent.store(false);
return true;
default:
@ -2712,9 +2721,14 @@ 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()) {
bool expected = false;
if (_pendingIdleEvent.compare_exchange_strong(expected, true)) {
postEvent(this, new QEvent((QEvent::Type)ApplicationEvent::Idle), Qt::HighEventPriority);
}
expected = false;
if (_renderEventHandler && !isAboutToQuit() && _pendingRenderEvent.compare_exchange_strong(expected, true)) {
postEvent(_renderEventHandler, new QEvent((QEvent::Type)ApplicationEvent::Render));
}
}
@ -2781,7 +2795,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 +2834,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
_pendingIdleEvent.store(false);
return true;
case QEvent::MouseMove:
@ -7203,7 +7246,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);

View file

@ -717,5 +717,8 @@ private:
LaserPointerManager _laserPointerManager;
friend class RenderEventHandler;
std::atomic<bool> _pendingIdleEvent { false };
std::atomic<bool> _pendingRenderEvent { false };
};
#endif // hifi_Application_h

View file

@ -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;

View file

@ -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),
@ -153,6 +157,7 @@ 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);
} else {
// note: PCM and no codec are identical
bool selectedPCM = _selectedCodecName == "pcm" || _selectedCodecName == "";
@ -160,20 +165,33 @@ int InboundAudioStream::parseData(ReceivedMessage& message) {
if (codecInPacket == _selectedCodecName || (packetPCM && selectedPCM)) {
auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead());
parseAudioData(message.getType(), afterProperties);
_mismatchedAudioCodecCount = 0;
} else {
qDebug(audio) << "Codec mismatch: expected" << _selectedCodecName << "got" << codecInPacket << "writing silence";
_mismatchedAudioCodecCount++;
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);
// inform others of the mismatch
auto sendingNode = DependencyManager::get<NodeList>()->nodeWithUUID(message.getSourceID());
if (sendingNode) {
emit mismatchedAudioCodec(sendingNode, _selectedCodecName, codecInPacket);
if (packetPCM) {
// 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 {
// 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 (_mismatchedAudioCodecCount > MAX_MISMATCHED_AUDIO_CODEC_COUNT) {
_mismatchedAudioCodecCount = 0;
// inform others of the mismatch
auto sendingNode = DependencyManager::get<NodeList>()->nodeWithUUID(message.getSourceID());
if (sendingNode) {
emit mismatchedAudioCodec(sendingNode, _selectedCodecName, codecInPacket);
qDebug(audio) << "Codec mismatch threshold exceeded, SelectedAudioFormat(" << _selectedCodecName << " ) sent";
}
}
}
}
break;

View file

@ -186,6 +186,7 @@ protected:
CodecPluginPointer _codec;
QString _selectedCodecName;
Decoder* _decoder { nullptr };
int _mismatchedAudioCodecCount { 0 };
};
float calculateRepeatedFrameFadeFactor(int indexOfRepeat);

View file

@ -983,6 +983,9 @@ public:
glm::vec2 dstCoord;
glm::ivec2 srcPixel;
for (int y = 0; y < faceWidth; ++y) {
QRgb* destScanLineBegin = reinterpret_cast<QRgb*>( 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<const QRgb*>(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;

View file

@ -1018,6 +1018,32 @@ void OffscreenQmlSurface::synthesizeKeyPress(QString key, QObject* targetOverrid
}
}
static void forEachKeyboard(QQuickItem* item, std::function<void(QQuickItem*)> function) {
QObject* itemObject = item;
while (itemObject) {
if (itemObject->parent()) {
itemObject = itemObject->parent();
} else {
break;
}
}
auto keyboards = itemObject->findChildren<QObject*>("keyboard");
for (auto keyboardObject : keyboards) {
auto keyboard = qobject_cast<QQuickItem*>(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,6 +1056,26 @@ 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<QQuickItem*>(object);
if (!item) {
return;
}
auto echoMode = item->property("echoMode");
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
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";

View file

@ -53,4 +53,8 @@ ScriptDiscoveryService.clearDebugWindow.connect(function() {
window.clearDebugWindow();
});
Script.scriptEnding.connect(function () {
window.close();
})
}());