From d9f12e44dc789384b3f8987c9b9033f96057ab32 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Fri, 12 Aug 2016 11:31:06 -0700 Subject: [PATCH 01/40] Add resampler support for downsampling with variable-input constant-output buffering mode --- libraries/audio/src/AudioSRC.cpp | 18 ++++++++++++++++++ libraries/audio/src/AudioSRC.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/libraries/audio/src/AudioSRC.cpp b/libraries/audio/src/AudioSRC.cpp index 5dba63b349..44326e5bc4 100644 --- a/libraries/audio/src/AudioSRC.cpp +++ b/libraries/audio/src/AudioSRC.cpp @@ -1030,3 +1030,21 @@ int AudioSRC::getMaxInput(int outputFrames) { return (int)(((int64_t)outputFrames * _step) >> 32); } } + +// the input frames that will produce exactly outputFrames +int AudioSRC::getExactInput(int outputFrames) { + // + // For upsampling, a correct implementation is more complicated + // because it requires early exit of the multirate filter. + // This is not currently supported. + // + if (_upFactor > _downFactor) { + return -1; + } + if (_step == 0) { + int64_t offset = ((int64_t)_phase * _downFactor) % _upFactor; + return (int)(((int64_t)outputFrames * _downFactor + offset) / _upFactor); + } else { + return (int)(((int64_t)outputFrames * _step + _offset) >> 32); + } +} diff --git a/libraries/audio/src/AudioSRC.h b/libraries/audio/src/AudioSRC.h index f7becc22c3..5ae5318b5e 100644 --- a/libraries/audio/src/AudioSRC.h +++ b/libraries/audio/src/AudioSRC.h @@ -48,6 +48,8 @@ public: int getMinInput(int outputFrames); int getMaxInput(int outputFrames); + int getExactInput(int outputFrames); + private: float* _polyphaseFilter; int* _stepTable; From 2326696d5430cdadfec6d85f575160a75c35415b Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 16 Aug 2016 16:53:51 -0700 Subject: [PATCH 02/40] Disabled the continuous polling for audio device changes on Windows. It necessitated extra buffering to prevent periodic dropouts in the audio stream. --- libraries/audio-client/src/AudioClient.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 398f4bb27c..0be91ab5fe 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1362,8 +1362,8 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { } void AudioClient::checkDevices() { -# ifdef Q_OS_LINUX - // on linux, this makes the audio stream hiccup +# if defined(Q_OS_LINUX) || defined (Q_OS_WIN) + // on Windows and Linux, this causes dropouts in the audio stream # else QVector inputDevices = getDeviceNames(QAudio::AudioInput); QVector outputDevices = getDeviceNames(QAudio::AudioOutput); From 7202d132c57b312fba19dac959abfd114f30236e Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 16 Aug 2016 16:57:28 -0700 Subject: [PATCH 03/40] Remove 22050 from the preferred list of back-end sample rates. --- libraries/audio-client/src/AudioClient.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 0be91ab5fe..3a049e0c38 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -334,20 +334,16 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat; const int FORTY_FOUR = 44100; + const int FORTY_EIGHT = 48000; adjustedAudioFormat = desiredAudioFormat; #ifdef Q_OS_ANDROID adjustedAudioFormat.setSampleRate(FORTY_FOUR); #else - const int HALF_FORTY_FOUR = FORTY_FOUR / 2; - - if (audioDevice.supportedSampleRates().contains(AudioConstants::SAMPLE_RATE * 2)) { - // use 48, which is a simple downsample, upsample - adjustedAudioFormat.setSampleRate(AudioConstants::SAMPLE_RATE * 2); - } else if (audioDevice.supportedSampleRates().contains(HALF_FORTY_FOUR)) { - // use 22050, resample but closer to 24 - adjustedAudioFormat.setSampleRate(HALF_FORTY_FOUR); + if (audioDevice.supportedSampleRates().contains(FORTY_EIGHT)) { + // use 48000, which is a simple downsample, upsample + adjustedAudioFormat.setSampleRate(FORTY_EIGHT); } else if (audioDevice.supportedSampleRates().contains(FORTY_FOUR)) { // use 44100, resample adjustedAudioFormat.setSampleRate(FORTY_FOUR); From f80304d68e9e3233ed54bc60314026483e32385b Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 18 Aug 2016 08:27:20 -0700 Subject: [PATCH 04/40] Change audio pipelines to process in exactly 10ms blocks (240 samples instead of 256). This produces an integral number of samples when resampled to 44.1k or 48k, allowing a 44.1k back-end to work correctly without extra buffering or dynamic buffers sizes. --- libraries/audio/src/AudioConstants.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioConstants.h b/libraries/audio/src/AudioConstants.h index 9271323498..b8ad94e669 100644 --- a/libraries/audio/src/AudioConstants.h +++ b/libraries/audio/src/AudioConstants.h @@ -28,9 +28,9 @@ namespace AudioConstants { const int MAX_CODEC_NAME_LENGTH = 30; const int MAX_CODEC_NAME_LENGTH_ON_WIRE = MAX_CODEC_NAME_LENGTH + sizeof(uint32_t); - const int NETWORK_FRAME_BYTES_STEREO = 1024; + const int NETWORK_FRAME_BYTES_STEREO = 960; const int NETWORK_FRAME_SAMPLES_STEREO = NETWORK_FRAME_BYTES_STEREO / sizeof(AudioSample); - const int NETWORK_FRAME_BYTES_PER_CHANNEL = 512; + const int NETWORK_FRAME_BYTES_PER_CHANNEL = NETWORK_FRAME_BYTES_STEREO / 2; const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample); const float NETWORK_FRAME_SECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL / float(AudioConstants::SAMPLE_RATE)); const float NETWORK_FRAME_MSECS = NETWORK_FRAME_SECS * 1000.0f; From 46c94a938ef5840268e22d011f7886329d4b4a1e Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 18 Aug 2016 08:29:32 -0700 Subject: [PATCH 05/40] Updated HRTF to process in 240-sample blocks --- libraries/audio/src/AudioHRTF.cpp | 62 +++++++++++++++---------------- libraries/audio/src/AudioHRTF.h | 2 +- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index 378e2154c1..5984187203 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -31,38 +31,36 @@ // Valimaki, Laakso. "Elimination of Transients in Time-Varying Allpass Fractional Delay Filters" // static const float crossfadeTable[HRTF_BLOCK] = { - 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, 0.9999611462f, 0.9998445910f, 0.9996503524f, - 0.9993784606f, 0.9990289579f, 0.9986018986f, 0.9980973490f, 0.9975153877f, 0.9968561049f, 0.9961196033f, 0.9953059972f, - 0.9944154131f, 0.9934479894f, 0.9924038765f, 0.9912832366f, 0.9900862439f, 0.9888130845f, 0.9874639561f, 0.9860390685f, - 0.9845386431f, 0.9829629131f, 0.9813121235f, 0.9795865307f, 0.9777864029f, 0.9759120199f, 0.9739636731f, 0.9719416652f, - 0.9698463104f, 0.9676779344f, 0.9654368743f, 0.9631234783f, 0.9607381059f, 0.9582811279f, 0.9557529262f, 0.9531538935f, - 0.9504844340f, 0.9477449623f, 0.9449359044f, 0.9420576968f, 0.9391107867f, 0.9360956322f, 0.9330127019f, 0.9298624749f, - 0.9266454408f, 0.9233620996f, 0.9200129616f, 0.9165985472f, 0.9131193872f, 0.9095760221f, 0.9059690029f, 0.9022988899f, - 0.8985662536f, 0.8947716742f, 0.8909157412f, 0.8869990541f, 0.8830222216f, 0.8789858616f, 0.8748906015f, 0.8707370778f, - 0.8665259359f, 0.8622578304f, 0.8579334246f, 0.8535533906f, 0.8491184090f, 0.8446291692f, 0.8400863689f, 0.8354907140f, - 0.8308429188f, 0.8261437056f, 0.8213938048f, 0.8165939546f, 0.8117449009f, 0.8068473974f, 0.8019022052f, 0.7969100928f, - 0.7918718361f, 0.7867882182f, 0.7816600290f, 0.7764880657f, 0.7712731319f, 0.7660160383f, 0.7607176017f, 0.7553786457f, - 0.7500000000f, 0.7445825006f, 0.7391269893f, 0.7336343141f, 0.7281053287f, 0.7225408922f, 0.7169418696f, 0.7113091309f, - 0.7056435516f, 0.6999460122f, 0.6942173981f, 0.6884585998f, 0.6826705122f, 0.6768540348f, 0.6710100717f, 0.6651395310f, - 0.6592433251f, 0.6533223705f, 0.6473775872f, 0.6414098993f, 0.6354202341f, 0.6294095226f, 0.6233786988f, 0.6173287002f, - 0.6112604670f, 0.6051749422f, 0.5990730716f, 0.5929558036f, 0.5868240888f, 0.5806788803f, 0.5745211331f, 0.5683518042f, - 0.5621718523f, 0.5559822381f, 0.5497839233f, 0.5435778714f, 0.5373650468f, 0.5311464151f, 0.5249229428f, 0.5186955971f, - 0.5124653459f, 0.5062331573f, 0.5000000000f, 0.4937668427f, 0.4875346541f, 0.4813044029f, 0.4750770572f, 0.4688535849f, - 0.4626349532f, 0.4564221286f, 0.4502160767f, 0.4440177619f, 0.4378281477f, 0.4316481958f, 0.4254788669f, 0.4193211197f, - 0.4131759112f, 0.4070441964f, 0.4009269284f, 0.3948250578f, 0.3887395330f, 0.3826712998f, 0.3766213012f, 0.3705904774f, - 0.3645797659f, 0.3585901007f, 0.3526224128f, 0.3466776295f, 0.3407566749f, 0.3348604690f, 0.3289899283f, 0.3231459652f, - 0.3173294878f, 0.3115414002f, 0.3057826019f, 0.3000539878f, 0.2943564484f, 0.2886908691f, 0.2830581304f, 0.2774591078f, - 0.2718946713f, 0.2663656859f, 0.2608730107f, 0.2554174994f, 0.2500000000f, 0.2446213543f, 0.2392823983f, 0.2339839617f, - 0.2287268681f, 0.2235119343f, 0.2183399710f, 0.2132117818f, 0.2081281639f, 0.2030899072f, 0.1980977948f, 0.1931526026f, - 0.1882550991f, 0.1834060454f, 0.1786061952f, 0.1738562944f, 0.1691570812f, 0.1645092860f, 0.1599136311f, 0.1553708308f, - 0.1508815910f, 0.1464466094f, 0.1420665754f, 0.1377421696f, 0.1334740641f, 0.1292629222f, 0.1251093985f, 0.1210141384f, - 0.1169777784f, 0.1130009459f, 0.1090842588f, 0.1052283258f, 0.1014337464f, 0.0977011101f, 0.0940309971f, 0.0904239779f, - 0.0868806128f, 0.0834014528f, 0.0799870384f, 0.0766379004f, 0.0733545592f, 0.0701375251f, 0.0669872981f, 0.0639043678f, - 0.0608892133f, 0.0579423032f, 0.0550640956f, 0.0522550377f, 0.0495155660f, 0.0468461065f, 0.0442470738f, 0.0417188721f, - 0.0392618941f, 0.0368765217f, 0.0345631257f, 0.0323220656f, 0.0301536896f, 0.0280583348f, 0.0260363269f, 0.0240879801f, - 0.0222135971f, 0.0204134693f, 0.0186878765f, 0.0170370869f, 0.0154613569f, 0.0139609315f, 0.0125360439f, 0.0111869155f, - 0.0099137561f, 0.0087167634f, 0.0075961235f, 0.0065520106f, 0.0055845869f, 0.0046940028f, 0.0038803967f, 0.0031438951f, - 0.0024846123f, 0.0019026510f, 0.0013981014f, 0.0009710421f, 0.0006215394f, 0.0003496476f, 0.0001554090f, 0.0000388538f, + 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, 1.0000000000f, + 0.9999545513f, 0.9998182135f, 0.9995910114f, 0.9992729863f, 0.9988641959f, 0.9983647147f, 0.9977746334f, 0.9970940592f, + 0.9963231160f, 0.9954619438f, 0.9945106993f, 0.9934695553f, 0.9923387012f, 0.9911183425f, 0.9898087010f, 0.9884100149f, + 0.9869225384f, 0.9853465419f, 0.9836823120f, 0.9819301512f, 0.9800903780f, 0.9781633270f, 0.9761493483f, 0.9740488082f, + 0.9718620885f, 0.9695895868f, 0.9672317161f, 0.9647889052f, 0.9622615981f, 0.9596502542f, 0.9569553484f, 0.9541773705f, + 0.9513168255f, 0.9483742335f, 0.9453501294f, 0.9422450630f, 0.9390595988f, 0.9357943158f, 0.9324498078f, 0.9290266826f, + 0.9255255626f, 0.9219470843f, 0.9182918983f, 0.9145606690f, 0.9107540747f, 0.9068728075f, 0.9029175730f, 0.8988890902f, + 0.8947880914f, 0.8906153223f, 0.8863715413f, 0.8820575200f, 0.8776740426f, 0.8732219061f, 0.8687019198f, 0.8641149055f, + 0.8594616970f, 0.8547431402f, 0.8499600930f, 0.8451134248f, 0.8402040169f, 0.8352327617f, 0.8302005629f, 0.8251083354f, + 0.8199570049f, 0.8147475079f, 0.8094807915f, 0.8041578130f, 0.7987795403f, 0.7933469510f, 0.7878610328f, 0.7823227830f, + 0.7767332084f, 0.7710933251f, 0.7654041585f, 0.7596667428f, 0.7538821211f, 0.7480513449f, 0.7421754743f, 0.7362555775f, + 0.7302927306f, 0.7242880178f, 0.7182425305f, 0.7121573680f, 0.7060336363f, 0.6998724488f, 0.6936749255f, 0.6874421931f, + 0.6811753847f, 0.6748756396f, 0.6685441031f, 0.6621819261f, 0.6557902652f, 0.6493702826f, 0.6429231452f, 0.6364500251f, + 0.6299520991f, 0.6234305485f, 0.6168865589f, 0.6103213199f, 0.6037360251f, 0.5971318716f, 0.5905100601f, 0.5838717943f, + 0.5772182810f, 0.5705507299f, 0.5638703530f, 0.5571783649f, 0.5504759820f, 0.5437644228f, 0.5370449075f, 0.5303186576f, + 0.5235868960f, 0.5168508463f, 0.5101117333f, 0.5033707820f, 0.4966292180f, 0.4898882667f, 0.4831491537f, 0.4764131040f, + 0.4696813424f, 0.4629550925f, 0.4562355772f, 0.4495240180f, 0.4428216351f, 0.4361296470f, 0.4294492701f, 0.4227817190f, + 0.4161282057f, 0.4094899399f, 0.4028681284f, 0.3962639749f, 0.3896786801f, 0.3831134411f, 0.3765694515f, 0.3700479009f, + 0.3635499749f, 0.3570768548f, 0.3506297174f, 0.3442097348f, 0.3378180739f, 0.3314558969f, 0.3251243604f, 0.3188246153f, + 0.3125578069f, 0.3063250745f, 0.3001275512f, 0.2939663637f, 0.2878426320f, 0.2817574695f, 0.2757119822f, 0.2697072694f, + 0.2637444225f, 0.2578245257f, 0.2519486551f, 0.2461178789f, 0.2403332572f, 0.2345958415f, 0.2289066749f, 0.2232667916f, + 0.2176772170f, 0.2121389672f, 0.2066530490f, 0.2012204597f, 0.1958421870f, 0.1905192085f, 0.1852524921f, 0.1800429951f, + 0.1748916646f, 0.1697994371f, 0.1647672383f, 0.1597959831f, 0.1548865752f, 0.1500399070f, 0.1452568598f, 0.1405383030f, + 0.1358850945f, 0.1312980802f, 0.1267780939f, 0.1223259574f, 0.1179424800f, 0.1136284587f, 0.1093846777f, 0.1052119086f, + 0.1011109098f, 0.0970824270f, 0.0931271925f, 0.0892459253f, 0.0854393310f, 0.0817081017f, 0.0780529157f, 0.0744744374f, + 0.0709733174f, 0.0675501922f, 0.0642056842f, 0.0609404012f, 0.0577549370f, 0.0546498706f, 0.0516257665f, 0.0486831745f, + 0.0458226295f, 0.0430446516f, 0.0403497458f, 0.0377384019f, 0.0352110948f, 0.0327682839f, 0.0304104132f, 0.0281379115f, + 0.0259511918f, 0.0238506517f, 0.0218366730f, 0.0199096220f, 0.0180698488f, 0.0163176880f, 0.0146534581f, 0.0130774616f, + 0.0115899851f, 0.0101912990f, 0.0088816575f, 0.0076612988f, 0.0065304447f, 0.0054893007f, 0.0045380562f, 0.0036768840f, + 0.0029059408f, 0.0022253666f, 0.0016352853f, 0.0011358041f, 0.0007270137f, 0.0004089886f, 0.0001817865f, 0.0000454487f, }; // diff --git a/libraries/audio/src/AudioHRTF.h b/libraries/audio/src/AudioHRTF.h index 63d1712980..08ae82b854 100644 --- a/libraries/audio/src/AudioHRTF.h +++ b/libraries/audio/src/AudioHRTF.h @@ -19,7 +19,7 @@ static const int HRTF_TAPS = 64; // minimum-phase FIR coefficients static const int HRTF_TABLES = 25; // number of HRTF subjects static const int HRTF_DELAY = 24; // max ITD in samples (1.0ms at 24KHz) -static const int HRTF_BLOCK = 256; // block processing size +static const int HRTF_BLOCK = 240; // block processing size static const float HRTF_GAIN = 1.0f; // HRTF global gain adjustment From 87d5ec2437a550b2fc92ef8008ed1323fad0f8e4 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 18 Aug 2016 08:31:43 -0700 Subject: [PATCH 06/40] Bump protocol version. Audio packets are now exactly 10ms (240/480 samples). --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index e9d61a827a..b852eed4d9 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -77,7 +77,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::InjectAudio: case PacketType::MicrophoneAudioNoEcho: case PacketType::MicrophoneAudioWithEcho: - return static_cast(AudioVersion::CodecNameInAudioPackets); + return static_cast(AudioVersion::Exactly10msAudioPackets); default: return 17; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 40524e2288..9b70c17a98 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -217,7 +217,8 @@ enum class DomainListVersion : PacketVersion { enum class AudioVersion : PacketVersion { HasCompressedAudio = 17, - CodecNameInAudioPackets + CodecNameInAudioPackets, + Exactly10msAudioPackets }; #endif // hifi_PacketHeaders_h From 3583bdecbb0d2bd6ba4e282bdbbd53772e0e5123 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 18 Aug 2016 16:56:26 -0700 Subject: [PATCH 07/40] Fix for calculateDeviceToNetworkInputRatio() which was completely broken. Running the audio back-end at 44.1k now works correctly. --- libraries/audio-client/src/AudioClient.cpp | 13 ++----------- libraries/audio-client/src/AudioClient.h | 1 - 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 3a049e0c38..cf43730258 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -748,12 +748,11 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } void AudioClient::handleAudioInput() { - const float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(); - const int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio); + // input samples required to produce exactly NETWORK_FRAME_SAMPLES of output + const int inputSamplesRequired = _inputFormat.channelCount() * _inputToNetworkResampler->getMinInput(AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); const auto inputAudioSamples = std::unique_ptr(new int16_t[inputSamplesRequired]); QByteArray inputByteArray = _inputDevice->readAll(); - handleLocalEchoAndReverb(inputByteArray); _inputRingBuffer.writeData(inputByteArray.data(), inputByteArray.size()); @@ -1261,14 +1260,6 @@ int AudioClient::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) return numInputCallbackBytes; } -float AudioClient::calculateDeviceToNetworkInputRatio() const { - float inputToNetworkInputRatio = (int)((_numInputCallbackBytes - * CALLBACK_ACCELERATOR_RATIO - / AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL) + 0.5f); - - return inputToNetworkInputRatio; -} - int AudioClient::calculateNumberOfFrameSamples(int numBytes) const { int frameSamples = (int)(numBytes * CALLBACK_ACCELERATOR_RATIO + 0.5f) / sizeof(int16_t); return frameSamples; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 171014edda..9db2cc0489 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -298,7 +298,6 @@ private: // Callback acceleration dependent calculations int calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const; int calculateNumberOfFrameSamples(int numBytes) const; - float calculateDeviceToNetworkInputRatio() const; quint16 _outgoingAvatarAudioSequenceNumber; From 4b2778f02dbc932c90a673fcf95f98c2df923b54 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 20 Aug 2016 12:22:47 -0700 Subject: [PATCH 08/40] Re-implement the continuous detection of device changes for all platforms, using a background thread. This prevents the glitches caused by calling checkDevices() on the audio processing thread. --- libraries/audio-client/src/AudioClient.cpp | 41 +++++++++++++++++----- libraries/audio-client/src/AudioClient.h | 3 +- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index cf43730258..8f8d17d4ad 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -67,6 +67,33 @@ Setting::Handle windowSecondsForDesiredReduction("windowSecondsForDesiredRe DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION); Setting::Handle repetitionWithFade("repetitionWithFade", DEFAULT_REPETITION_WITH_FADE); +// background thread that continuously polls for device changes +class CheckDevicesThread : public QThread { +public: + const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000; + + CheckDevicesThread(AudioClient* audioClient) + : _audioClient(audioClient) { + + connect(qApp, &QCoreApplication::aboutToQuit, [this] { + _quit = true; + }); + } + + void run() override { + while (!_quit) { + + //qDebug() << "Checking for audio device changes..."; + _audioClient->checkDevices(); + + QThread::msleep(DEVICE_CHECK_INTERVAL_MSECS); + } + } + + AudioClient* _audioClient { nullptr }; + bool _quit { false }; +}; + AudioClient::AudioClient() : AbstractAudioInterface(), _audioInput(NULL), @@ -121,14 +148,14 @@ AudioClient::AudioClient() : connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat); - _inputDevices = getDeviceNames(QAudio::AudioInput); _outputDevices = getDeviceNames(QAudio::AudioOutput); - const qint64 DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000; - QTimer* updateTimer = new QTimer(this); - connect(updateTimer, &QTimer::timeout, this, &AudioClient::checkDevices); - updateTimer->start(DEVICE_CHECK_INTERVAL_MSECS); + // start a thread to detect any device changes + QThread* checkDevicesThread = new CheckDevicesThread(this); + checkDevicesThread->setObjectName("CheckDevices Thread"); + checkDevicesThread->setPriority(QThread::LowPriority); + checkDevicesThread->start(); configureReverb(); @@ -1349,9 +1376,6 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { } void AudioClient::checkDevices() { -# if defined(Q_OS_LINUX) || defined (Q_OS_WIN) - // on Windows and Linux, this causes dropouts in the audio stream -# else QVector inputDevices = getDeviceNames(QAudio::AudioInput); QVector outputDevices = getDeviceNames(QAudio::AudioOutput); @@ -1361,7 +1385,6 @@ void AudioClient::checkDevices() { emit deviceChanged(); } -# endif } void AudioClient::loadSettings() { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 9db2cc0489..1ee4b92a0c 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -139,6 +139,8 @@ public: QVector& getActiveLocalAudioInjectors() { return _activeLocalAudioInjectors; } + void checkDevices(); + static const float CALLBACK_ACCELERATOR_RATIO; #ifdef Q_OS_WIN @@ -312,7 +314,6 @@ private: QVector _inputDevices; QVector _outputDevices; - void checkDevices(); bool _hasReceivedFirstPacket = false; From 8fbf5a9e8baf7a85493ceefcd8570ff76fa2067e Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 24 Aug 2016 10:16:43 -0700 Subject: [PATCH 09/40] PR feedback --- libraries/audio-client/src/AudioClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 8f8d17d4ad..1e324e7bec 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -90,6 +90,7 @@ public: } } +private: AudioClient* _audioClient { nullptr }; bool _quit { false }; }; From b4d143fe130f3318e5ab4d5dbbdbc6f70be8526f Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 24 Aug 2016 10:19:09 -0700 Subject: [PATCH 10/40] PR feedback --- libraries/audio-client/src/AudioClient.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 1e324e7bec..131c55e6d1 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -82,10 +82,7 @@ public: void run() override { while (!_quit) { - - //qDebug() << "Checking for audio device changes..."; _audioClient->checkDevices(); - QThread::msleep(DEVICE_CHECK_INTERVAL_MSECS); } } From c0caea1ffd72265660567910c18882a1823567b5 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 24 Aug 2016 10:32:29 -0700 Subject: [PATCH 11/40] PR feedback --- libraries/audio-client/src/AudioClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 131c55e6d1..089ef75095 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1373,6 +1373,7 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { return bytesWritten; } +// now called from a background thread, to keep blocking operations off the audio thread void AudioClient::checkDevices() { QVector inputDevices = getDeviceNames(QAudio::AudioInput); QVector outputDevices = getDeviceNames(QAudio::AudioOutput); From 1ab35797b16c6c1b6b4cf636b20756d0aeebc42a Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Thu, 25 Aug 2016 16:09:25 -0700 Subject: [PATCH 12/40] Drastically reduce the audio capture buffering on Windows. With device detection on another thread and WASAPI back-end, the large capture buffer (resulting from CALLBACK_ACCELERATOR_RATIO) is no longer needed. --- libraries/audio-client/src/AudioClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 089ef75095..f16b662428 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1265,7 +1265,7 @@ int AudioClient::setOutputBufferSize(int numFrames, bool persist) { // proportional to the accelerator ratio. #ifdef Q_OS_WIN -const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 0.1f; +const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 1.0f; #endif #ifdef Q_OS_MAC From 996bf0d763b9c4d7ac2328f97d238d950869ffc4 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sun, 28 Aug 2016 20:12:59 -0700 Subject: [PATCH 13/40] Replace the Qt multimedia audio back-end on the Windows build. Adds an external project (wasapi) to download a low-latency audio plugin for Qt, as a precompiled DLL. Adds a post-build command to use this new plugin instead of the default Qt audio plugin. --- cmake/externals/wasapi/CMakeLists.txt | 24 +++++++++++++++++++ .../PackageLibrariesForDeployment.cmake | 10 ++++++++ interface/CMakeLists.txt | 1 + 3 files changed, 35 insertions(+) create mode 100644 cmake/externals/wasapi/CMakeLists.txt diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt new file mode 100644 index 0000000000..87c3f4b0e4 --- /dev/null +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -0,0 +1,24 @@ +if (WIN32) + + set(EXTERNAL_NAME wasapi) + string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + + include(ExternalProject) + ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi.zip + URL_MD5 11c8a7728d6eda7223df800e10b70723 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + ) + + # Hide this external target (for ide users) + set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + + ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + + set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR} CACHE FILEPATH "Location of wasapi DLL") + +endif() diff --git a/cmake/macros/PackageLibrariesForDeployment.cmake b/cmake/macros/PackageLibrariesForDeployment.cmake index 050cea9fe1..7f826c1f8c 100644 --- a/cmake/macros/PackageLibrariesForDeployment.cmake +++ b/cmake/macros/PackageLibrariesForDeployment.cmake @@ -41,5 +41,15 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT) POST_BUILD COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$,$,$>:--release> $" ) + + set(QTAUDIO_PATH $/audio) + + # if present, replace qtaudio_windows.dll with qtaudio_wasapi.dll + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND if exist ${QTAUDIO_PATH}/qtaudio_windows.dll ( ${CMAKE_COMMAND} -E remove ${QTAUDIO_PATH}/qtaudio_windows.dll && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.dll ${QTAUDIO_PATH} && ${CMAKE_COMMAND} -E copy ${WASAPI_DLL_PATH}/qtaudio_wasapi.pdb ${QTAUDIO_PATH} ) + ) + endif () endmacro() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 414fafe705..27cba520bb 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -126,6 +126,7 @@ if (WIN32) add_dependency_external_projects(sdl2) add_dependency_external_projects(OpenVR) add_dependency_external_projects(neuron) + add_dependency_external_projects(wasapi) endif() # disable /OPT:REF and /OPT:ICF for the Debug builds From 678ea3ecbfbf08f00213310b696e58eb28fb42fc Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 29 Aug 2016 13:22:48 -0700 Subject: [PATCH 14/40] PR feedback --- cmake/externals/wasapi/CMakeLists.txt | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/cmake/externals/wasapi/CMakeLists.txt b/cmake/externals/wasapi/CMakeLists.txt index 87c3f4b0e4..019920df77 100644 --- a/cmake/externals/wasapi/CMakeLists.txt +++ b/cmake/externals/wasapi/CMakeLists.txt @@ -1,24 +1,24 @@ if (WIN32) - set(EXTERNAL_NAME wasapi) - string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) + set(EXTERNAL_NAME wasapi) + string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) - include(ExternalProject) - ExternalProject_Add( - ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi.zip - URL_MD5 11c8a7728d6eda7223df800e10b70723 - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - LOG_DOWNLOAD 1 - ) + include(ExternalProject) + ExternalProject_Add( + ${EXTERNAL_NAME} + URL http://hifi-public.s3.amazonaws.com/dependencies/qtaudio_wasapi.zip + URL_MD5 11c8a7728d6eda7223df800e10b70723 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD 1 + ) - # Hide this external target (for ide users) - set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") + # Hide this external target (for ide users) + set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) + ExternalProject_Get_Property(${EXTERNAL_NAME} SOURCE_DIR) - set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR} CACHE FILEPATH "Location of wasapi DLL") + set(${EXTERNAL_NAME_UPPER}_DLL_PATH ${SOURCE_DIR} CACHE FILEPATH "Location of wasapi DLL") endif() From d2b33e00bdbccabcc06497be51c018239ad850a1 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 30 Aug 2016 10:13:08 -0700 Subject: [PATCH 15/40] Fix audio startup failures. Directly calling the static method QAudioDeviceInfo::availableDevices() was not thread-safe. --- libraries/audio-client/src/AudioClient.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index f16b662428..02b9cdfd39 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -82,8 +82,8 @@ public: void run() override { while (!_quit) { - _audioClient->checkDevices(); QThread::msleep(DEVICE_CHECK_INTERVAL_MSECS); + _audioClient->checkDevices(); } } @@ -195,10 +195,17 @@ void AudioClient::audioMixerKilled() { emit disconnected(); } +// thread-safe +QList getAvailableDevices(QAudio::Mode mode) { + static std::mutex mutex; + std::lock_guard lock(mutex); + + return QAudioDeviceInfo::availableDevices(mode); +} QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) { QAudioDeviceInfo result; - foreach(QAudioDeviceInfo audioDevice, QAudioDeviceInfo::availableDevices(mode)) { + foreach(QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { if (audioDevice.deviceName().trimmed() == deviceName.trimmed()) { result = audioDevice; break; @@ -262,7 +269,7 @@ QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) { QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { #ifdef __APPLE__ - if (QAudioDeviceInfo::availableDevices(mode).size() > 1) { + if (getAvailableDevices(mode).size() > 1) { AudioDeviceID defaultDeviceID = 0; uint32_t propertySize = sizeof(AudioDeviceID); AudioObjectPropertyAddress propertyAddress = { @@ -292,7 +299,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { if (!getPropertyError && propertySize) { // find a device in the list that matches the name we have and return it - foreach(QAudioDeviceInfo audioDevice, QAudioDeviceInfo::availableDevices(mode)) { + foreach(QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { if (audioDevice.deviceName() == CFStringGetCStringPtr(deviceName, kCFStringEncodingMacRoman)) { return audioDevice; } @@ -601,7 +608,7 @@ QString AudioClient::getDefaultDeviceName(QAudio::Mode mode) { QVector AudioClient::getDeviceNames(QAudio::Mode mode) { QVector deviceNames; - foreach(QAudioDeviceInfo audioDevice, QAudioDeviceInfo::availableDevices(mode)) { + foreach(QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { deviceNames << audioDevice.deviceName().trimmed(); } return deviceNames; From 9187b4aa7d77e01bc015b73186b865feb6dc2eb7 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 30 Aug 2016 12:57:29 -0700 Subject: [PATCH 16/40] More complete fix for audio threading failures. Qt keeps an internal Q_GLOBAL_STATIC list of devices, which is referenced at device start() but clobbered by availableDevices(). Now protected by a mutex. --- libraries/audio-client/src/AudioClient.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 02b9cdfd39..db61ecd5f1 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -67,6 +67,11 @@ Setting::Handle windowSecondsForDesiredReduction("windowSecondsForDesiredRe DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION); Setting::Handle repetitionWithFade("repetitionWithFade", DEFAULT_REPETITION_WITH_FADE); +// protect the Qt internal device list +using Mutex = std::mutex; +using Lock = std::unique_lock; +static Mutex _deviceMutex; + // background thread that continuously polls for device changes class CheckDevicesThread : public QThread { public: @@ -197,9 +202,8 @@ void AudioClient::audioMixerKilled() { // thread-safe QList getAvailableDevices(QAudio::Mode mode) { - static std::mutex mutex; - std::lock_guard lock(mutex); - + // NOTE: availableDevices() clobbers the Qt internal device list + Lock lock(_deviceMutex); return QAudioDeviceInfo::availableDevices(mode); } @@ -741,7 +745,11 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { if (!_loopbackOutputDevice && _loopbackAudioOutput) { // we didn't have the loopback output device going so set that up now + + // NOTE: device start() uses the Qt internal device list + Lock lock(_deviceMutex); _loopbackOutputDevice = _loopbackAudioOutput->start(); + lock.unlock(); if (!_loopbackOutputDevice) { return; @@ -1133,7 +1141,10 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes); _inputRingBuffer.resizeForFrameSize(numFrameSamples); + // NOTE: device start() uses the Qt internal device list + Lock lock(_deviceMutex); _inputDevice = _audioInput->start(); + lock.unlock(); if (_inputDevice) { connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleAudioInput())); @@ -1229,7 +1240,11 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify); _audioOutputIODevice.start(); + + // NOTE: device start() uses the Qt internal device list + Lock lock(_deviceMutex); _audioOutput->start(&_audioOutputIODevice); + lock.unlock(); qCDebug(audioclient) << "Output Buffer capacity in frames: " << _audioOutput->bufferSize() / sizeof(int16_t) / (float)_outputFrameSize << "requested bytes:" << requestedSize << "actual bytes:" << _audioOutput->bufferSize() << From cb5bded7c16c67e6e43f58b19b73ceb84e676e28 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 30 Aug 2016 14:37:33 -0700 Subject: [PATCH 17/40] Add support for more back-end sample rates --- libraries/audio-client/src/AudioClient.cpp | 24 ++++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index db61ecd5f1..84396fa079 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -365,24 +365,26 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, const QAudioFormat& desiredAudioFormat, QAudioFormat& adjustedAudioFormat) { - // FIXME: directly using 24khz has a bug somewhere that causes channels to be swapped. - // Continue using our internal resampler, for now. qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat; - const int FORTY_FOUR = 44100; - const int FORTY_EIGHT = 48000; adjustedAudioFormat = desiredAudioFormat; #ifdef Q_OS_ANDROID - adjustedAudioFormat.setSampleRate(FORTY_FOUR); + adjustedAudioFormat.setSampleRate(44100); #else - if (audioDevice.supportedSampleRates().contains(FORTY_EIGHT)) { - // use 48000, which is a simple downsample, upsample - adjustedAudioFormat.setSampleRate(FORTY_EIGHT); - } else if (audioDevice.supportedSampleRates().contains(FORTY_FOUR)) { - // use 44100, resample - adjustedAudioFormat.setSampleRate(FORTY_FOUR); + // + // Attempt to set the device sample rate in decreasing order of preference. + // On Windows, using WASAPI shared mode, only a match with the hardware sample rate will succeed. + // + if (audioDevice.supportedSampleRates().contains(48000)) { + adjustedAudioFormat.setSampleRate(48000); + } else if (audioDevice.supportedSampleRates().contains(44100)) { + adjustedAudioFormat.setSampleRate(44100); + } else if (audioDevice.supportedSampleRates().contains(32000)) { + adjustedAudioFormat.setSampleRate(32000); + } else if (audioDevice.supportedSampleRates().contains(24000)) { + adjustedAudioFormat.setSampleRate(24000); } #endif From 1a6af49b3d28f4af2337396184c820ad5a0a529e Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Tue, 30 Aug 2016 16:10:11 -0700 Subject: [PATCH 18/40] Support for even more back-end sample rates --- libraries/audio-client/src/AudioClient.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 84396fa079..7fdda5a118 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -374,7 +374,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, #else // - // Attempt to set the device sample rate in decreasing order of preference. + // Attempt the device sample rate in decreasing order of preference. // On Windows, using WASAPI shared mode, only a match with the hardware sample rate will succeed. // if (audioDevice.supportedSampleRates().contains(48000)) { @@ -385,6 +385,16 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, adjustedAudioFormat.setSampleRate(32000); } else if (audioDevice.supportedSampleRates().contains(24000)) { adjustedAudioFormat.setSampleRate(24000); + } else if (audioDevice.supportedSampleRates().contains(16000)) { + adjustedAudioFormat.setSampleRate(16000); + } else if (audioDevice.supportedSampleRates().contains(96000)) { + adjustedAudioFormat.setSampleRate(96000); + } else if (audioDevice.supportedSampleRates().contains(192000)) { + adjustedAudioFormat.setSampleRate(192000); + } else if (audioDevice.supportedSampleRates().contains(88200)) { + adjustedAudioFormat.setSampleRate(88200); + } else if (audioDevice.supportedSampleRates().contains(176400)) { + adjustedAudioFormat.setSampleRate(176400); } #endif From 72535ce237aaf8d28ebcfcd1ce96ff9676388f56 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Wed, 31 Aug 2016 13:09:06 -0700 Subject: [PATCH 19/40] Fix reverbTest.js which was broken due to missing helper script --- scripts/developer/utilities/tools/cookies.js | 1536 ++++++++++++++++++ 1 file changed, 1536 insertions(+) create mode 100644 scripts/developer/utilities/tools/cookies.js diff --git a/scripts/developer/utilities/tools/cookies.js b/scripts/developer/utilities/tools/cookies.js new file mode 100644 index 0000000000..0e08f80d0e --- /dev/null +++ b/scripts/developer/utilities/tools/cookies.js @@ -0,0 +1,1536 @@ +// +// cookies.js +// +// version 2.0 +// +// Created by Sam Gateau, 4/1/2015 +// A simple ui panel that present a list of porperties and the proper widget to edit it +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; + +var SLIDER_RANGE_INCREMENT_SCALE = 1 / 1000; +var THUMB_COLOR = { + red: 150, + green: 150, + blue: 150 +}; +var THUMB_HIGHLIGHT = { + red: 255, + green: 255, + blue: 255 +}; +var CHECK_MARK_COLOR = { + red: 70, + green: 70, + blue: 90 +}; + +// The Slider class +(function() { + var Slider = function(x, y, width, thumbSize) { + + this.background = Overlays.addOverlay("text", { + backgroundColor: { + red: 200, + green: 200, + blue: 255 + }, + x: x, + y: y, + width: width, + height: thumbSize, + alpha: 1.0, + backgroundAlpha: 0.5, + visible: true + }); + this.thumb = Overlays.addOverlay("text", { + backgroundColor: THUMB_COLOR, + x: x, + y: y, + width: thumbSize, + height: thumbSize, + alpha: 1.0, + backgroundAlpha: 1.0, + visible: true + }); + + this.thumbSize = thumbSize; + this.thumbHalfSize = 0.5 * thumbSize; + + this.minThumbX = x + this.thumbHalfSize; + this.maxThumbX = x + width - this.thumbHalfSize; + this.thumbX = this.minThumbX; + + this.minValue = 0.0; + this.maxValue = 1.0; + + this.y = y; + + this.clickOffsetX = 0; + this.isMoving = false; + this.visible = true; + }; + + Slider.prototype.updateThumb = function() { + var thumbTruePos = this.thumbX - 0.5 * this.thumbSize; + Overlays.editOverlay(this.thumb, { + x: thumbTruePos + }); + }; + + Slider.prototype.isClickableOverlayItem = function(item) { + return (item == this.thumb) || (item == this.background); + }; + + + Slider.prototype.onMousePressEvent = function(event, clickedOverlay) { + if (!this.isClickableOverlayItem(clickedOverlay)) { + this.isMoving = false; + return; + } + this.highlight(); + this.isMoving = true; + var clickOffset = event.x - this.thumbX; + if ((clickOffset > -this.thumbHalfSize) && (clickOffset < this.thumbHalfSize)) { + this.clickOffsetX = clickOffset; + } else { + this.clickOffsetX = 0; + this.thumbX = event.x; + this.updateThumb(); + this.onValueChanged(this.getValue()); + } + }; + + Slider.prototype.onMouseMoveEvent = function(event) { + if (this.isMoving) { + var newThumbX = event.x - this.clickOffsetX; + if (newThumbX < this.minThumbX) { + newThumbX = this.minThumbX; + } + if (newThumbX > this.maxThumbX) { + newThumbX = this.maxThumbX; + } + this.thumbX = newThumbX; + this.updateThumb(); + this.onValueChanged(this.getValue()); + } + }; + + Slider.prototype.onMouseReleaseEvent = function(event) { + this.isMoving = false; + this.unhighlight(); + }; + + Slider.prototype.updateWithKeys = function(direction) { + this.range = this.maxThumbX - this.minThumbX; + this.thumbX += direction * (this.range * SCALE); + this.updateThumb(); + this.onValueChanged(this.getValue()); + }; + + Slider.prototype.highlight = function() { + if (this.highlighted) { + return; + } + Overlays.editOverlay(this.thumb, { + backgroundColor: { + red: 255, + green: 255, + blue: 255 + } + }); + this.highlighted = true; + }; + + Slider.prototype.unhighlight = function() { + if (!this.highlighted) { + return; + } + Overlays.editOverlay(this.thumb, { + backgroundColor: THUMB_COLOR + }); + this.highlighted = false; + }; + + Slider.prototype.setNormalizedValue = function(value) { + if (value < 0.0) { + this.thumbX = this.minThumbX; + } else if (value > 1.0) { + this.thumbX = this.maxThsumbX; + } else { + this.thumbX = value * (this.maxThumbX - this.minThumbX) + this.minThumbX; + } + this.updateThumb(); + }; + Slider.prototype.getNormalizedValue = function() { + return (this.thumbX - this.minThumbX) / (this.maxThumbX - this.minThumbX); + }; + + Slider.prototype.setValue = function(value) { + var normValue = (value - this.minValue) / (this.maxValue - this.minValue); + this.setNormalizedValue(normValue); + }; + Slider.prototype.getValue = function() { + return this.getNormalizedValue() * (this.maxValue - this.minValue) + this.minValue; + }; + + Slider.prototype.reset = function(resetValue) { + this.setValue(resetValue); + this.onValueChanged(resetValue); + }; + + + Slider.prototype.setMinValue = function(minValue) { + var currentValue = this.getValue(); + this.minValue = minValue; + this.setValue(currentValue); + }; + Slider.prototype.getMinValue = function() { + return this.minValue; + }; + Slider.prototype.setMaxValue = function(maxValue) { + var currentValue = this.getValue(); + this.maxValue = maxValue; + this.setValue(currentValue); + }; + Slider.prototype.getMaxValue = function() { + return this.maxValue; + }; + + Slider.prototype.onValueChanged = function(value) {}; + + Slider.prototype.getHeight = function() { + if (!this.visible) { + return 0; + } + return 1.5 * this.thumbSize; + }; + + Slider.prototype.moveUp = function(newY) { + Overlays.editOverlay(this.background, { + y: newY + }); + Overlays.editOverlay(this.thumb, { + y: newY + }); + }; + + Slider.prototype.moveDown = function() { + Overlays.editOverlay(this.background, { + y: this.y + }); + Overlays.editOverlay(this.thumb, { + y: this.y + }); + }; + + this.setThumbColor = function(color) { + Overlays.editOverlay(this.thumb, { + backgroundColor: { + red: color.x * 255, + green: color.y * 255, + blue: color.z * 255 + } + }); + }; + this.setBackgroundColor = function(color) { + Overlays.editOverlay(this.background, { + backgroundColor: { + red: color.x * 255, + green: color.y * 255, + blue: color.z * 255 + } + }); + }; + + Slider.prototype.hide = function() { + Overlays.editOverlay(this.background, { + visible: false + }); + Overlays.editOverlay(this.thumb, { + visible: false + }); + this.visible = false; + }; + + Slider.prototype.show = function() { + Overlays.editOverlay(this.background, { + visible: true + }); + Overlays.editOverlay(this.thumb, { + visible: true + }); + this.visible = true; + }; + + Slider.prototype.destroy = function() { + Overlays.deleteOverlay(this.background); + Overlays.deleteOverlay(this.thumb); + }; + + this.Slider = Slider; + + // The Checkbox class + var Checkbox = function(x, y, width, thumbSize) { + + this.thumb = Overlays.addOverlay("text", { + backgroundColor: THUMB_COLOR, + textFontSize: 10, + x: x, + y: y, + width: thumbSize, + height: thumbSize, + alpha: 1.0, + backgroundAlpha: 1.0, + visible: true + }); + + + this.thumbSize = thumbSize; + var checkX = x + (0.25 * thumbSize); + var checkY = y + (0.25 * thumbSize); + this.y = y; + this.boxCheckStatus = true; + this.clickedBox = false; + this.visible = true; + + + this.checkMark = Overlays.addOverlay("text", { + backgroundColor: CHECK_MARK_COLOR, + x: checkX, + y: checkY, + width: thumbSize / 2.0, + height: thumbSize / 2.0, + alpha: 1.0, + visible: true + }); + }; + + Checkbox.prototype.updateThumb = function() { + Overlays.editOverlay(this.checkMark, { + visible: this.boxCheckStatus + }); + }; + + Checkbox.prototype.isClickableOverlayItem = function(item) { + return (item == this.thumb) || (item == this.checkMark); + }; + + Checkbox.prototype.onMousePressEvent = function(event, clickedOverlay) { + if (!this.isClickableOverlayItem(clickedOverlay)) { + return; + } + this.boxCheckStatus = !this.boxCheckStatus; + this.onValueChanged(this.getValue()); + this.updateThumb(); + }; + + Checkbox.prototype.onMouseReleaseEvent = function(event) {}; + + Checkbox.prototype.updateWithKeys = function() { + this.boxCheckStatus = !this.boxCheckStatus; + this.onValueChanged(this.getValue()); + this.updateThumb(); + }; + + Checkbox.prototype.highlight = function() { + Overlays.editOverlay(this.thumb, { + backgroundColor: THUMB_HIGHLIGHT + }); + this.highlighted = true; + }; + + Checkbox.prototype.unhighlight = function() { + Overlays.editOverlay(this.thumb, { + backgroundColor: THUMB_COLOR + }); + this.highlighted = false; + }; + + Checkbox.prototype.setValue = function(value) { + this.boxCheckStatus = value; + }; + + Checkbox.prototype.setterFromWidget = function(value) { + this.updateThumb(); + }; + + Checkbox.prototype.getValue = function() { + return this.boxCheckStatus; + }; + + Checkbox.prototype.reset = function(resetValue) { + this.setValue(resetValue); + }; + + Checkbox.prototype.onValueChanged = function(value) {}; + + Checkbox.prototype.getHeight = function() { + if (!this.visible) { + return 0; + } + return 1.5 * this.thumbSize; + }; + + Checkbox.prototype.moveUp = function(newY) { + Overlays.editOverlay(this.background, { + y: newY + }); + Overlays.editOverlay(this.thumb, { + y: newY + }); + Overlays.editOverlay(this.checkMark, { + y: newY + (0.25 * this.thumbSize) + }); + Overlays.editOverlay(this.unCheckMark, { + y: newY + (0.25 * this.thumbSize) + }); + }; + + Checkbox.prototype.moveDown = function() { + Overlays.editOverlay(this.background, { + y: this.y + }); + Overlays.editOverlay(this.thumb, { + y: this.y + }); + Overlays.editOverlay(this.checkMark, { + y: this.y + (0.25 * this.thumbSize) + }); + Overlays.editOverlay(this.unCheckMark, { + y: this.y+ (0.25 * this.thumbSize) + }); + }; + + Checkbox.prototype.hide = function() { + Overlays.editOverlay(this.background, { + visible: false + }); + Overlays.editOverlay(this.thumb, { + visible: false + }); + Overlays.editOverlay(this.checkMark, { + visible: false + }); + Overlays.editOverlay(this.unCheckMark, { + visible: false + }); + this.visible = false; + } + + Checkbox.prototype.show = function() { + Overlays.editOverlay(this.background, { + visible: true + }); + Overlays.editOverlay(this.thumb, { + visible: true + }); + Overlays.editOverlay(this.checkMark, { + visible: true + }); + Overlays.editOverlay(this.unCheckMark, { + visible: true + }); + this.visible = true; + } + + Checkbox.prototype.destroy = function() { + Overlays.deleteOverlay(this.background); + Overlays.deleteOverlay(this.thumb); + Overlays.deleteOverlay(this.checkMark); + Overlays.deleteOverlay(this.unCheckMark); + }; + + this.Checkbox = Checkbox; + + // The ColorBox class + var ColorBox = function(x, y, width, thumbSize) { + var self = this; + + var slideHeight = thumbSize / 3; + var sliderWidth = width; + this.red = new Slider(x, y, width, slideHeight); + this.green = new Slider(x, y + slideHeight, width, slideHeight); + this.blue = new Slider(x, y + 2 * slideHeight, width, slideHeight); + this.red.setBackgroundColor({ + x: 1, + y: 0, + z: 0 + }); + this.green.setBackgroundColor({ + x: 0, + y: 1, + z: 0 + }); + this.blue.setBackgroundColor({ + x: 0, + y: 0, + z: 1 + }); + + this.red.onValueChanged = this.setterFromWidget; + this.green.onValueChanged = this.setterFromWidget; + this.blue.onValueChanged = this.setterFromWidget; + + this.visible = true; + }; + + ColorBox.prototype.isClickableOverlayItem = function(item) { + return this.red.isClickableOverlayItem(item) || this.green.isClickableOverlayItem(item) || this.blue.isClickableOverlayItem(item); + }; + + ColorBox.prototype.onMousePressEvent = function(event, clickedOverlay) { + this.red.onMousePressEvent(event, clickedOverlay); + if (this.red.isMoving) { + return; + } + + this.green.onMousePressEvent(event, clickedOverlay); + if (this.green.isMoving) { + return; + } + + this.blue.onMousePressEvent(event, clickedOverlay); + }; + + ColorBox.prototype.onMouseMoveEvent = function(event) { + this.red.onMouseMoveEvent(event); + this.green.onMouseMoveEvent(event); + this.blue.onMouseMoveEvent(event); + }; + + ColorBox.prototype.onMouseReleaseEvent = function(event) { + this.red.onMouseReleaseEvent(event); + this.green.onMouseReleaseEvent(event); + this.blue.onMouseReleaseEvent(event); + }; + + ColorBox.prototype.updateWithKeys = function(direction) { + this.red.updateWithKeys(direction); + this.green.updateWithKeys(direction); + this.blue.updateWithKeys(direction); + } + + ColorBox.prototype.highlight = function() { + this.red.highlight(); + this.green.highlight(); + this.blue.highlight(); + + this.highlighted = true; + }; + + ColorBox.prototype.unhighlight = function() { + this.red.unhighlight(); + this.green.unhighlight(); + this.blue.unhighlight(); + + this.highlighted = false; + }; + + ColorBox.prototype.setterFromWidget = function(value) { + var color = this.getValue(); + this.onValueChanged(color); + this.updateRGBSliders(color); + }; + + ColorBox.prototype.onValueChanged = function(value) {}; + + ColorBox.prototype.updateRGBSliders = function(color) { + this.red.setThumbColor({ + x: color.x, + y: 0, + z: 0 + }); + this.green.setThumbColor({ + x: 0, + y: color.y, + z: 0 + }); + this.blue.setThumbColor({ + x: 0, + y: 0, + z: color.z + }); + }; + + // Public members: + ColorBox.prototype.setValue = function(value) { + this.red.setValue(value.x); + this.green.setValue(value.y); + this.blue.setValue(value.z); + this.updateRGBSliders(value); + }; + + ColorBox.prototype.getValue = function() { + var value = { + x: this.red.getValue(), + y: this.green.getValue(), + z: this.blue.getValue() + }; + return value; + }; + + ColorBox.prototype.reset = function(resetValue) { + this.setValue(resetValue); + this.onValueChanged(resetValue); + }; + + + ColorBox.prototype.getHeight = function() { + if (!this.visible) { + return 0; + } + return 1.5 * this.thumbSize; + }; + + ColorBox.prototype.moveUp = function(newY) { + this.red.moveUp(newY); + this.green.moveUp(newY); + this.blue.moveUp(newY); + }; + + ColorBox.prototype.moveDown = function() { + this.red.moveDown(); + this.green.moveDown(); + this.blue.moveDown(); + }; + + ColorBox.prototype.hide = function() { + this.red.hide(); + this.green.hide(); + this.blue.hide(); + this.visible = false; + } + + ColorBox.prototype.show = function() { + this.red.show(); + this.green.show(); + this.blue.show(); + this.visible = true; + } + + ColorBox.prototype.destroy = function() { + this.red.destroy(); + this.green.destroy(); + this.blue.destroy(); + }; + + this.ColorBox = ColorBox; + + // The DirectionBox class + var DirectionBox = function(x, y, width, thumbSize) { + var self = this; + + var slideHeight = thumbSize / 2; + var sliderWidth = width; + this.yaw = new Slider(x, y, width, slideHeight); + this.pitch = new Slider(x, y + slideHeight, width, slideHeight); + + + this.yaw.setThumbColor({ + x: 1, + y: 0, + z: 0 + }); + this.yaw.minValue = -180; + this.yaw.maxValue = +180; + + this.pitch.setThumbColor({ + x: 0, + y: 0, + z: 1 + }); + this.pitch.minValue = -1; + this.pitch.maxValue = +1; + + this.yaw.onValueChanged = this.setterFromWidget; + this.pitch.onValueChanged = this.setterFromWidget; + + this.visible = true; + }; + + DirectionBox.prototype.isClickableOverlayItem = function(item) { + return this.yaw.isClickableOverlayItem(item) || this.pitch.isClickableOverlayItem(item); + }; + + DirectionBox.prototype.onMousePressEvent = function(event, clickedOverlay) { + this.yaw.onMousePressEvent(event, clickedOverlay); + if (this.yaw.isMoving) { + return; + } + this.pitch.onMousePressEvent(event, clickedOverlay); + }; + + DirectionBox.prototype.onMouseMoveEvent = function(event) { + this.yaw.onMouseMoveEvent(event); + this.pitch.onMouseMoveEvent(event); + }; + + DirectionBox.prototype.onMouseReleaseEvent = function(event) { + this.yaw.onMouseReleaseEvent(event); + this.pitch.onMouseReleaseEvent(event); + }; + + DirectionBox.prototype.updateWithKeys = function(direction) { + this.yaw.updateWithKeys(direction); + this.pitch.updateWithKeys(direction); + }; + + DirectionBox.prototype.highlight = function() { + this.pitch.highlight(); + this.yaw.highlight(); + + this.highlighted = true; + }; + + DirectionBox.prototype.unhighlight = function() { + this.pitch.unhighlight(); + this.yaw.unhighlight(); + + this.highlighted = false; + }; + + DirectionBox.prototype.setterFromWidget = function(value) { + var yawPitch = this.getValue(); + this.onValueChanged(yawPitch); + }; + + DirectionBox.prototype.onValueChanged = function(value) {}; + + DirectionBox.prototype.setValue = function(direction) { + var flatXZ = Math.sqrt(direction.x * direction.x + direction.z * direction.z); + if (flatXZ > 0.0) { + var flatX = direction.x / flatXZ; + var flatZ = direction.z / flatXZ; + var yaw = Math.acos(flatX) * 180 / Math.PI; + if (flatZ < 0) { + yaw = -yaw; + } + this.yaw.setValue(yaw); + } + this.pitch.setValue(direction.y); + }; + + DirectionBox.prototype.getValue = function() { + var dirZ = this.pitch.getValue(); + var yaw = this.yaw.getValue() * Math.PI / 180; + var cosY = Math.sqrt(1 - dirZ * dirZ); + var value = { + x: cosY * Math.cos(yaw), + y: dirZ, + z: cosY * Math.sin(yaw) + }; + return value; + }; + + DirectionBox.prototype.reset = function(resetValue) { + this.setValue(resetValue); + this.onValueChanged(resetValue); + }; + + DirectionBox.prototype.getHeight = function() { + if (!this.visible) { + return 0; + } + return 1.5 * this.thumbSize; + }; + + DirectionBox.prototype.moveUp = function(newY) { + this.pitch.moveUp(newY); + this.yaw.moveUp(newY); + }; + + DirectionBox.prototype.moveDown = function(newY) { + this.pitch.moveDown(); + this.yaw.moveDown(); + }; + + DirectionBox.prototype.hide = function() { + this.pitch.hide(); + this.yaw.hide(); + this.visible = false; + } + + DirectionBox.prototype.show = function() { + this.pitch.show(); + this.yaw.show(); + this.visible = true; + } + + DirectionBox.prototype.destroy = function() { + this.yaw.destroy(); + this.pitch.destroy(); + }; + + this.DirectionBox = DirectionBox; + + var textFontSize = 12; + + var CollapsablePanelItem = function(name, x, y, textWidth, height) { + this.name = name; + this.height = height; + this.y = y; + this.isCollapsable = true; + + var topMargin = (height - 1.5 * textFontSize); + + this.thumb = Overlays.addOverlay("image", { + color: { + red: 255, + green: 255, + blue: 255 + }, + imageURL: HIFI_PUBLIC_BUCKET + 'images/tools/expand-ui.svg', + x: x, + y: y, + width: rawHeight, + height: rawHeight, + alpha: 1.0, + visible: true + }); + + this.title = Overlays.addOverlay("text", { + backgroundColor: { + red: 255, + green: 255, + blue: 255 + }, + x: x + rawHeight * 1.5, + y: y, + width: textWidth, + height: height, + alpha: 1.0, + backgroundAlpha: 0.5, + visible: true, + text: " " + name, + font: { + size: textFontSize + }, + topMargin: topMargin + }); + }; + + CollapsablePanelItem.prototype.destroy = function() { + Overlays.deleteOverlay(this.title); + Overlays.deleteOverlay(this.thumb); + }; + + CollapsablePanelItem.prototype.editTitle = function(opts) { + Overlays.editOverlay(this.title, opts); + }; + + CollapsablePanelItem.prototype.hide = function() { + Overlays.editOverlay(this.title, { + visible: false + }); + Overlays.editOverlay(this.thumb, { + visible: false + }); + + if (this.widget != null) { + this.widget.hide(); + } + }; + + CollapsablePanelItem.prototype.show = function() { + Overlays.editOverlay(this.title, { + visible: true + }); + Overlays.editOverlay(this.thumb, { + visible: true + }); + + if (this.widget != null) { + this.widget.show(); + } + }; + + CollapsablePanelItem.prototype.moveUp = function(newY) { + Overlays.editOverlay(this.title, { + y: newY + }); + Overlays.editOverlay(this.thumb, { + y: newY + }); + + if (this.widget != null) { + this.widget.moveUp(newY); + } + } + + CollapsablePanelItem.prototype.moveDown = function() { + Overlays.editOverlay(this.title, { + y: this.y + }); + Overlays.editOverlay(this.thumb, { + y: this.y + }); + + if (this.widget != null) { + this.widget.moveDown(); + } + } + this.CollapsablePanelItem = CollapsablePanelItem; + + var PanelItem = function(name, setter, getter, displayer, x, y, textWidth, valueWidth, height) { + //print("creating panel item: " + name); + + this.isCollapsable = false; + this.name = name; + this.y = y; + this.isCollapsed = false; + + this.displayer = typeof displayer !== 'undefined' ? displayer : function(value) { + if (value == true) { + return "On"; + } else if (value == false) { + return "Off"; + } + return value.toFixed(2); + }; + + var topMargin = (height - 1.5 * textFontSize); + this.title = Overlays.addOverlay("text", { + backgroundColor: { + red: 255, + green: 255, + blue: 255 + }, + x: x, + y: y, + width: textWidth, + height: height, + alpha: 1.0, + backgroundAlpha: 0.5, + visible: true, + text: " " + name, + font: { + size: textFontSize + }, + topMargin: topMargin + }); + + this.value = Overlays.addOverlay("text", { + backgroundColor: { + red: 255, + green: 255, + blue: 255 + }, + x: x + textWidth, + y: y, + width: valueWidth, + height: height, + alpha: 1.0, + backgroundAlpha: 0.5, + visible: true, + text: this.displayer(getter()), + font: { + size: textFontSize + }, + topMargin: topMargin + }); + + this.getter = getter; + this.resetValue = getter(); + + this.setter = function(value) { + + setter(value); + + Overlays.editOverlay(this.value, { + text: this.displayer(getter()) + }); + + if (this.widget) { + this.widget.setValue(value); + } + + //print("successfully set value of widget to " + value); + }; + this.setterFromWidget = function(value) { + setter(value); + // ANd loop back the value after the final setter has been called + var value = getter(); + + if (this.widget) { + this.widget.setValue(value); + } + Overlays.editOverlay(this.value, { + text: this.displayer(value) + }); + }; + + this.widget = null; + }; + + PanelItem.prototype.hide = function() { + Overlays.editOverlay(this.title, { + visible: false + }); + Overlays.editOverlay(this.value, { + visible: false + }); + + if (this.widget != null) { + this.widget.hide(); + } + }; + + + PanelItem.prototype.show = function() { + Overlays.editOverlay(this.title, { + visible: true + }); + Overlays.editOverlay(this.value, { + visible: true + }); + + if (this.widget != null) { + this.widget.show(); + } + + }; + + PanelItem.prototype.moveUp = function(newY) { + + Overlays.editOverlay(this.title, { + y: newY + }); + Overlays.editOverlay(this.value, { + y: newY + }); + + if (this.widget != null) { + this.widget.moveUp(newY); + } + + }; + + PanelItem.prototype.moveDown = function() { + + Overlays.editOverlay(this.title, { + y: this.y + }); + Overlays.editOverlay(this.value, { + y: this.y + }); + + if (this.widget != null) { + this.widget.moveDown(); + } + + }; + + PanelItem.prototype.destroy = function() { + Overlays.deleteOverlay(this.title); + Overlays.deleteOverlay(this.value); + + if (this.widget != null) { + this.widget.destroy(); + } + }; + this.PanelItem = PanelItem; + + var textWidth = 180; + var valueWidth = 100; + var widgetWidth = 300; + var rawHeight = 20; + var rawYDelta = rawHeight * 1.5; + var outerPanel = true; + var widgets; + + + var Panel = function(x, y) { + + if (outerPanel) { + widgets = []; + } + outerPanel = false; + + this.x = x; + this.y = y; + this.nextY = y; + + print("creating panel at x: " + this.x + " y: " + this.y); + + this.widgetX = x + textWidth + valueWidth; + + this.items = new Array(); + this.activeWidget = null; + this.visible = true; + this.indentation = 30; + + }; + + Panel.prototype.mouseMoveEvent = function(event) { + if (this.activeWidget) { + this.activeWidget.onMouseMoveEvent(event); + } + }; + + Panel.prototype.mousePressEvent = function(event) { + // Make sure we quitted previous widget + if (this.activeWidget) { + this.activeWidget.onMouseReleaseEvent(event); + } + this.activeWidget = null; + + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + + this.handleCollapse(clickedOverlay); + + // If the user clicked any of the slider background then... + for (var i in this.items) { + var item = this.items[i]; + var widget = this.items[i].widget; + + if (widget.isClickableOverlayItem(clickedOverlay)) { + this.activeWidget = widget; + this.activeWidget.onMousePressEvent(event, clickedOverlay); + break; + + } + } + }; + + Panel.prototype.mouseReleaseEvent = function(event) { + if (this.activeWidget) { + this.activeWidget.onMouseReleaseEvent(event); + } + this.activeWidget = null; + }; + + // Reset panel item upon double-clicking + Panel.prototype.mouseDoublePressEvent = function(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + this.handleReset(clickedOverlay); + }; + + + Panel.prototype.handleReset = function(clickedOverlay) { + for (var i in this.items) { + + var item = this.items[i]; + var widget = item.widget; + + if (item.isSubPanel && widget) { + widget.handleReset(clickedOverlay); + } + + if (clickedOverlay == item.title) { + item.activeWidget = widget; + item.activeWidget.reset(item.resetValue); + break; + } + } + }; + + Panel.prototype.handleCollapse = function(clickedOverlay) { + for (var i in this.items) { + + var item = this.items[i]; + var widget = item.widget; + + if (item.isSubPanel && widget) { + widget.handleCollapse(clickedOverlay); + } + + if (!item.isCollapsed && item.isCollapsable && clickedOverlay == item.thumb) { + Overlays.editOverlay(item.thumb, { + imageURL: HIFI_PUBLIC_BUCKET + 'images/tools/expand-right.svg' + }); + this.collapse(clickedOverlay); + item.isCollapsed = true; + } else if (item.isCollapsed && item.isCollapsable && clickedOverlay == item.thumb) { + Overlays.editOverlay(item.thumb, { + imageURL: HIFI_PUBLIC_BUCKET + 'images/tools/expand-ui.svg' + }); + this.expand(clickedOverlay); + item.isCollapsed = false; + } + } + }; + + Panel.prototype.collapse = function(clickedOverlay) { + var keys = Object.keys(this.items); + + for (var i = 0; i < keys.length; ++i) { + var item = this.items[keys[i]]; + + if (item.isCollapsable && clickedOverlay == item.thumb) { + var panel = item.widget; + panel.hide(); + break; + } + } + + // Now recalculate new heights of subsequent widgets + for (var j = i + 1; j < keys.length; ++j) { + this.items[keys[j]].moveUp(this.getCurrentY(keys[j])); + } + }; + + Panel.prototype.expand = function(clickedOverlay) { + var keys = Object.keys(this.items); + + for (var i = 0; i < keys.length; ++i) { + var item = this.items[keys[i]]; + + if (item.isCollapsable && clickedOverlay == item.thumb) { + var panel = item.widget; + panel.show(); + break; + } + } + // Now recalculate new heights of subsequent widgets + for (var j = i + 1; j < keys.length; ++j) { + this.items[keys[j]].moveDown(); + } + }; + + + Panel.prototype.onMousePressEvent = function(event, clickedOverlay) { + for (var i in this.items) { + var item = this.items[i]; + if (item.widget.isClickableOverlayItem(clickedOverlay)) { + item.activeWidget = item.widget; + item.activeWidget.onMousePressEvent(event, clickedOverlay); + } + } + }; + + Panel.prototype.onMouseMoveEvent = function(event) { + for (var i in this.items) { + var item = this.items[i]; + if (item.activeWidget) { + item.activeWidget.onMouseMoveEvent(event); + } + } + }; + + Panel.prototype.onMouseReleaseEvent = function(event, clickedOverlay) { + for (var i in this.items) { + var item = this.items[i]; + if (item.activeWidget) { + item.activeWidget.onMouseReleaseEvent(event); + } + item.activeWidget = null; + } + }; + + Panel.prototype.onMouseDoublePressEvent = function(event, clickedOverlay) { + for (var i in this.items) { + var item = this.items[i]; + if (item.activeWidget) { + item.activeWidget.onMouseDoublePressEvent(event); + } + } + }; + + var tabView = false; + var tabIndex = 0; + + Panel.prototype.keyPressEvent = function(event) { + if (event.text == "TAB" && !event.isShifted) { + tabView = true; + if (tabIndex < widgets.length) { + if (tabIndex > 0 && widgets[tabIndex - 1].highlighted) { + // Unhighlight previous widget + widgets[tabIndex - 1].unhighlight(); + } + widgets[tabIndex].highlight(); + tabIndex++; + } else { + widgets[tabIndex - 1].unhighlight(); + //Wrap around to front + tabIndex = 0; + widgets[tabIndex].highlight(); + tabIndex++; + } + } else if (tabView && event.isShifted) { + if (tabIndex > 0) { + tabIndex--; + if (tabIndex < widgets.length && widgets[tabIndex + 1].highlighted) { + // Unhighlight previous widget + widgets[tabIndex + 1].unhighlight(); + } + widgets[tabIndex].highlight(); + } else { + widgets[tabIndex].unhighlight(); + //Wrap around to end + tabIndex = widgets.length - 1; + widgets[tabIndex].highlight(); + } + } else if (event.text == "LEFT") { + for (var i = 0; i < widgets.length; i++) { + // Find the highlighted widget, allow control with arrow keys + if (widgets[i].highlighted) { + var k = -1; + widgets[i].updateWithKeys(k); + break; + } + } + } else if (event.text == "RIGHT") { + for (var i = 0; i < widgets.length; i++) { + // Find the highlighted widget, allow control with arrow keys + if (widgets[i].highlighted) { + var k = 1; + widgets[i].updateWithKeys(k); + break; + } + } + } + }; + + // Widget constructors + Panel.prototype.newSlider = function(name, minValue, maxValue, setValue, getValue, displayValue) { + this.nextY = this.y + this.getHeight(); + + var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight); + var slider = new Slider(this.widgetX, this.nextY, widgetWidth, rawHeight); + slider.minValue = minValue; + slider.maxValue = maxValue; + widgets.push(slider); + + item.widget = slider; + item.widget.onValueChanged = function(value) { + item.setterFromWidget(value); + }; + item.setter(getValue()); + this.items[name] = item; + + }; + + Panel.prototype.newCheckbox = function(name, setValue, getValue, displayValue) { + var display; + if (displayValue == true) { + display = function() { + return "On"; + }; + } else if (displayValue == false) { + display = function() { + return "Off"; + }; + } + + this.nextY = this.y + this.getHeight(); + + var item = new PanelItem(name, setValue, getValue, display, this.x, this.nextY, textWidth, valueWidth, rawHeight); + + var checkbox = new Checkbox(this.widgetX, this.nextY, widgetWidth, rawHeight); + widgets.push(checkbox); + + item.widget = checkbox; + item.widget.onValueChanged = function(value) { + item.setterFromWidget(value); + }; + item.setter(getValue()); + this.items[name] = item; + + //print("created Item... checkbox=" + name); + }; + + Panel.prototype.newColorBox = function(name, setValue, getValue, displayValue) { + this.nextY = this.y + this.getHeight(); + + var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight); + + var colorBox = new ColorBox(this.widgetX, this.nextY, widgetWidth, rawHeight); + widgets.push(colorBox); + + item.widget = colorBox; + item.widget.onValueChanged = function(value) { + item.setterFromWidget(value); + }; + item.setter(getValue()); + this.items[name] = item; + + // print("created Item... colorBox=" + name); + }; + + Panel.prototype.newDirectionBox = function(name, setValue, getValue, displayValue) { + this.nextY = this.y + this.getHeight(); + + var item = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight); + + var directionBox = new DirectionBox(this.widgetX, this.nextY, widgetWidth, rawHeight); + widgets.push(directionBox); + + item.widget = directionBox; + item.widget.onValueChanged = function(value) { + item.setterFromWidget(value); + }; + item.setter(getValue()); + this.items[name] = item; + + // print("created Item... directionBox=" + name); + }; + + Panel.prototype.newSubPanel = function(name) { + + this.nextY = this.y + this.getHeight(); + + var item = new CollapsablePanelItem(name, this.x, this.nextY, textWidth, rawHeight); + item.isSubPanel = true; + + this.nextY += 1.5 * item.height; + + var subPanel = new Panel(this.x + this.indentation, this.nextY); + + item.widget = subPanel; + this.items[name] = item; + return subPanel; + // print("created Item... subPanel=" + name); + }; + + Panel.prototype.onValueChanged = function(value) { + for (var i in this.items) { + this.items[i].widget.onValueChanged(value); + } + }; + + + Panel.prototype.set = function(name, value) { + var item = this.items[name]; + if (item != null) { + return item.setter(value); + } + return null; + }; + + Panel.prototype.get = function(name) { + var item = this.items[name]; + if (item != null) { + return item.getter(); + } + return null; + }; + + Panel.prototype.getWidget = function(name) { + var item = this.items[name]; + if (item != null) { + return item.widget; + } + return null; + }; + + Panel.prototype.update = function(name) { + var item = this.items[name]; + if (item != null) { + return item.setter(item.getter()); + } + return null; + }; + + Panel.prototype.isClickableOverlayItem = function(item) { + for (var i in this.items) { + if (this.items[i].widget.isClickableOverlayItem(item)) { + return true; + } + } + return false; + }; + + Panel.prototype.getHeight = function() { + var height = 0; + + for (var i in this.items) { + height += this.items[i].widget.getHeight(); + if (this.items[i].isSubPanel && this.items[i].widget.visible) { + height += 1.5 * rawHeight; + } + } + + return height; + }; + + Panel.prototype.moveUp = function() { + for (var i in this.items) { + this.items[i].widget.moveUp(); + } + }; + + Panel.prototype.moveDown = function() { + for (var i in this.items) { + this.items[i].widget.moveDown(); + } + }; + + Panel.prototype.getCurrentY = function(key) { + var height = 0; + var keys = Object.keys(this.items); + + for (var i = 0; i < keys.indexOf(key); ++i) { + var item = this.items[keys[i]]; + + height += item.widget.getHeight(); + + if (item.isSubPanel) { + height += 1.5 * rawHeight; + + } + } + return this.y + height; + }; + + + Panel.prototype.hide = function() { + for (var i in this.items) { + if (this.items[i].isSubPanel) { + this.items[i].widget.hide(); + } + this.items[i].hide(); + } + this.visible = false; + }; + + Panel.prototype.show = function() { + for (var i in this.items) { + if (this.items[i].isSubPanel) { + this.items[i].widget.show(); + } + this.items[i].show(); + } + this.visible = true; + }; + + + Panel.prototype.destroy = function() { + for (var i in this.items) { + + if (this.items[i].isSubPanel) { + this.items[i].widget.destroy(); + } + this.items[i].destroy(); + } + }; + this.Panel = Panel; +})(); + + +Script.scriptEnding.connect(function scriptEnding() { + Controller.releaseKeyEvents({ + text: "left" + }); + Controller.releaseKeyEvents({ + key: "right" + }); +}); + + +Controller.captureKeyEvents({ + text: "left" +}); +Controller.captureKeyEvents({ + text: "right" +}); From 20deae32429a0b962f1a3c51074ce0fa2273deec Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 23 Jan 2016 23:14:41 -0800 Subject: [PATCH 20/40] Working on Qt 5.6 compatibility --- interface/CMakeLists.txt | 8 +++---- interface/src/scripting/WebWindowClass.cpp | 11 ++++----- interface/src/scripting/WebWindowClass.h | 4 ++-- interface/src/ui/DataWebPage.cpp | 28 +++++++++++----------- interface/src/ui/DataWebPage.h | 10 ++++---- libraries/ui/src/VrMenu.cpp | 11 ++++----- 6 files changed, 35 insertions(+), 37 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 1d436695b1..b6e9153f65 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -42,12 +42,12 @@ endif () if (ANDROID) set(PLATFORM_QT_COMPONENTS AndroidExtras) else () - set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets WebKitWidgets) + set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets) endif () find_package( Qt5 COMPONENTS - Gui Multimedia Network OpenGL Qml Quick Script ScriptTools Svg + Gui Multimedia Network OpenGL Qml Quick Script Svg ${PLATFORM_QT_COMPONENTS} WebChannel WebSockets ) @@ -241,8 +241,8 @@ include_directories("${PROJECT_SOURCE_DIR}/src") target_link_libraries( ${TARGET_NAME} Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL - Qt5::Qml Qt5::Quick Qt5::Script Qt5::ScriptTools Qt5::Svg - Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets Qt5::WebKitWidgets + Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg + Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets ) # Issue causes build failure unless we add this directory. diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index 18beee4bbf..400c538461 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -14,8 +14,7 @@ #include #include #include -#include -#include +#include #include #include @@ -48,7 +47,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid layout->setContentsMargins(0, 0, 0, 0); dialogWidget->setLayout(layout); - _webView = new QWebView(dialogWidget); + _webView = new QWebEngineView(dialogWidget); layout->addWidget(_webView); @@ -69,8 +68,8 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid } connect(this, &WebWindowClass::destroyed, _windowWidget, &QWidget::deleteLater); - connect(_webView->page()->mainFrame(), &QWebFrame::javaScriptWindowObjectCleared, - this, &WebWindowClass::addEventBridgeToWindowObject); + //connect(_webView->page()->mainFrame(), &QWebFrame::javaScriptWindowObjectCleared, + // this, &WebWindowClass::addEventBridgeToWindowObject); } WebWindowClass::~WebWindowClass() { @@ -94,7 +93,7 @@ void WebWindowClass::hasClosed() { } void WebWindowClass::addEventBridgeToWindowObject() { - _webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge); +// _webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge); } void WebWindowClass::setVisible(bool visible) { diff --git a/interface/src/scripting/WebWindowClass.h b/interface/src/scripting/WebWindowClass.h index d7a610dd39..fdb434ab7e 100644 --- a/interface/src/scripting/WebWindowClass.h +++ b/interface/src/scripting/WebWindowClass.h @@ -14,7 +14,7 @@ #include #include -#include +#include class ScriptEventBridge : public QObject { Q_OBJECT @@ -73,7 +73,7 @@ private slots: private: QWidget* _windowWidget; - QWebView* _webView; + QWebEngineView* _webView; ScriptEventBridge* _eventBridge; }; diff --git a/interface/src/ui/DataWebPage.cpp b/interface/src/ui/DataWebPage.cpp index 01feacc393..7168dfc5d6 100644 --- a/interface/src/ui/DataWebPage.cpp +++ b/interface/src/ui/DataWebPage.cpp @@ -17,23 +17,22 @@ #include "DataWebPage.h" -DataWebPage::DataWebPage(QObject* parent) : - QWebPage(parent) +DataWebPage::DataWebPage(QObject* parent) : QWebEnginePage(parent) { // use an OAuthNetworkAccessManager instead of regular QNetworkAccessManager so our requests are authed - setNetworkAccessManager(OAuthNetworkAccessManager::getInstance()); +// setNetworkAccessManager(OAuthNetworkAccessManager::getInstance()); // give the page an empty stylesheet - settings()->setUserStyleSheetUrl(QUrl()); +// settings()->setUserStyleSheetUrl(QUrl()); } -void DataWebPage::javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID) { - qDebug() << "JS console message at line" << lineNumber << "from" << sourceID << "-" << message; -} +//void DataWebPage::javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID) { +// qDebug() << "JS console message at line" << lineNumber << "from" << sourceID << "-" << message; +//} -bool DataWebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type) { +bool DataWebPage::acceptNavigationRequest(const QUrl & url, NavigationType type, bool isMainFrame) { // Handle hifi:// links and links to files with particular extensions - QString urlString = request.url().toString(); + QString urlString = url.toString(); if (qApp->canAcceptURL(urlString)) { if (qApp->acceptURL(urlString)) { return false; // we handled it, so QWebPage doesn't need to handle it @@ -41,14 +40,15 @@ bool DataWebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkReques } // Make hyperlinks with target="_blank" open in user's Web browser - if (type == QWebPage::NavigationTypeLinkClicked && frame == nullptr) { - qApp->openUrl(request.url()); + if (type == NavigationTypeLinkClicked && !isMainFrame) { + qApp->openUrl(url); return false; // We handled it. } return true; } -QString DataWebPage::userAgentForUrl(const QUrl& url) const { - return HIGH_FIDELITY_USER_AGENT; -} +// +//QString DataWebPage::userAgentForUrl(const QUrl& url) const { +// return HIGH_FIDELITY_USER_AGENT; +//} diff --git a/interface/src/ui/DataWebPage.h b/interface/src/ui/DataWebPage.h index f9aa5be8a8..55e185ca4f 100644 --- a/interface/src/ui/DataWebPage.h +++ b/interface/src/ui/DataWebPage.h @@ -12,15 +12,15 @@ #ifndef hifi_DataWebPage_h #define hifi_DataWebPage_h -#include +#include -class DataWebPage : public QWebPage { +class DataWebPage : public QWebEnginePage { public: DataWebPage(QObject* parent = 0); protected: - void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID) override; - bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type) override; - virtual QString userAgentForUrl(const QUrl& url) const override; + //virtual void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID) override; + virtual bool acceptNavigationRequest(const QUrl & url, NavigationType type, bool isMainFrame) override; + //virtual QString userAgentForUrl(const QUrl& url) const override; }; #endif // hifi_DataWebPage_h diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index c2732197d3..f4b365265d 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -155,7 +155,7 @@ void bindActionToQmlAction(QObject* qmlAction, QAction* action) { QObject::connect(qmlAction, SIGNAL(triggered()), action, SLOT(trigger())); } -class QQuickMenuItem; +class QQuickMenuItem1; void VrMenu::addAction(QMenu* menu, QAction* action) { Q_ASSERT(!MenuUserData::hasData(action)); @@ -167,10 +167,9 @@ void VrMenu::addAction(QMenu* menu, QAction* action) { } QObject* menuQml = findMenuObject(userData->uuid.toString()); Q_ASSERT(menuQml); - QQuickMenuItem* returnedValue { nullptr }; - + QQuickMenuItem1* returnedValue { nullptr }; bool invokeResult = QMetaObject::invokeMethod(menuQml, "addItem", Qt::DirectConnection, - Q_RETURN_ARG(QQuickMenuItem*, returnedValue), + Q_RETURN_ARG(QQuickMenuItem1*, returnedValue), Q_ARG(QString, action->text())); Q_ASSERT(invokeResult); @@ -206,10 +205,10 @@ void VrMenu::insertAction(QAction* before, QAction* action) { beforeQml = findMenuObject(beforeUserData->uuid.toString()); } QObject* menu = beforeQml->parent(); - QQuickMenuItem* returnedValue { nullptr }; + QQuickMenuItem1* returnedValue { nullptr }; // FIXME this needs to find the index of the beforeQml item and call insertItem(int, object) bool invokeResult = QMetaObject::invokeMethod(menu, "addItem", Qt::DirectConnection, - Q_RETURN_ARG(QQuickMenuItem*, returnedValue), + Q_RETURN_ARG(QQuickMenuItem1*, returnedValue), Q_ARG(QString, action->text())); Q_ASSERT(invokeResult); QObject* result = reinterpret_cast(returnedValue); // returnedValue.value(); From 9d447d4dccdb797b77d2006c1189c4d6077ce84f Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 1 Jul 2016 16:04:57 -0700 Subject: [PATCH 21/40] enabled qnetworkrequest redirects everywhere I could find them --- domain-server/src/DomainServer.cpp | 2 ++ ice-server/src/IceServer.cpp | 1 + interface/src/Application.cpp | 3 ++- interface/src/ui/ModelsBrowser.cpp | 2 ++ interface/src/ui/ScriptEditorWidget.cpp | 1 + libraries/auto-updater/src/AutoUpdater.cpp | 1 + libraries/avatars/src/AvatarData.cpp | 1 + libraries/fbx/src/FSTReader.cpp | 1 + libraries/fbx/src/OBJReader.cpp | 1 + libraries/networking/src/AccountManager.cpp | 4 +++- libraries/networking/src/AddressManager.cpp | 1 + libraries/networking/src/HTTPResourceRequest.cpp | 1 + libraries/networking/src/OAuthNetworkAccessManager.cpp | 1 + libraries/script-engine/src/ScriptsModel.cpp | 1 + libraries/script-engine/src/XMLHttpRequestClass.cpp | 1 + tests/gpu-test/src/TestFbx.cpp | 1 + 16 files changed, 21 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index b61fef8525..570db05871 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1756,6 +1756,7 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u .arg(authorizationCode, oauthRedirectURL().toString(), _oauthClientID, _oauthClientSecret); QNetworkRequest tokenRequest(tokenRequestUrl); + tokenRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); tokenRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); @@ -1949,6 +1950,7 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR profileURL.setQuery(QString("%1=%2").arg(OAUTH_JSON_ACCESS_TOKEN_KEY, accessToken)); QNetworkRequest profileRequest(profileURL); + profileRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); profileRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); return NetworkAccessManager::getInstance().get(profileRequest); } diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index 84c48ac5f9..3f229d2f87 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -213,6 +213,7 @@ void IceServer::requestDomainPublicKey(const QUuid& domainID) { publicKeyURL.setPath(publicKeyPath); QNetworkRequest publicKeyRequest { publicKeyURL }; + publicKeyRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); publicKeyRequest.setAttribute(QNetworkRequest::User, domainID); qDebug() << "Requesting public key for domain with ID" << domainID; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 45feed1088..c22e04a2e4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5034,6 +5034,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { bool Application::askToWearAvatarAttachmentUrl(const QString& url) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(networkRequest); int requestNumber = ++_avatarAttachmentRequest; @@ -5741,4 +5742,4 @@ void Application::updateThreadPoolCount() const { qDebug() << "Reserved threads " << reservedThreads; qDebug() << "Setting thread pool size to " << threadPoolSize; QThreadPool::globalInstance()->setMaxThreadCount(threadPoolSize); -} \ No newline at end of file +} diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 91de4e36ac..430cc805ed 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -228,6 +228,7 @@ void ModelHandler::update() { QUrl url(_model.item(i,0)->data(Qt::UserRole).toString()); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.head(request); connect(reply, SIGNAL(finished()), SLOT(downloadFinished())); @@ -280,6 +281,7 @@ void ModelHandler::queryNewFiles(QString marker) { url.setQuery(query); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(request); connect(reply, SIGNAL(finished()), SLOT(downloadFinished())); diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp index f76a5dff78..ada6b11355 100644 --- a/interface/src/ui/ScriptEditorWidget.cpp +++ b/interface/src/ui/ScriptEditorWidget.cpp @@ -161,6 +161,7 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) { } else { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(networkRequest); qDebug() << "Downloading included script at" << scriptPath; diff --git a/libraries/auto-updater/src/AutoUpdater.cpp b/libraries/auto-updater/src/AutoUpdater.cpp index ea4e43ff41..43563b1d71 100644 --- a/libraries/auto-updater/src/AutoUpdater.cpp +++ b/libraries/auto-updater/src/AutoUpdater.cpp @@ -33,6 +33,7 @@ void AutoUpdater::checkForUpdate() { void AutoUpdater::getLatestVersionData() { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest latestVersionRequest(BUILDS_XML_URL); + latestVersionRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(latestVersionRequest); connect(reply, &QNetworkReply::finished, this, &AutoUpdater::parseLatestVersionData); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 037f12d2fa..413180738a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1206,6 +1206,7 @@ void AvatarData::updateJointMappings() { if (_skeletonModelURL.fileName().toLower().endsWith(".fst")) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL); + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* networkReply = networkAccessManager.get(networkRequest); connect(networkReply, &QNetworkReply::finished, this, &AvatarData::setJointMappingsFromNetworkReply); diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 6f5d0d7ec5..1f7999bdaa 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -191,6 +191,7 @@ FSTReader::ModelType FSTReader::predictModelType(const QVariantHash& mapping) { QVariantHash FSTReader::downloadMapping(const QString& url) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(networkRequest); qDebug() << "Downloading avatar file at " << url; diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 38121555ed..02b0afdf26 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -282,6 +282,7 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) { }); QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest netRequest(url); + netRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); QNetworkReply* netReply = isTest ? networkAccessManager.head(netRequest) : networkAccessManager.get(netRequest); if (!qApp || aboutToQuit) { netReply->deleteLater(); diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 2f4cbad26c..1c3850f8cb 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -218,7 +218,7 @@ void AccountManager::sendRequest(const QString& path, QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest; - + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter()); networkRequest.setRawHeader(METAVERSE_SESSION_ID_HEADER, @@ -484,6 +484,7 @@ void AccountManager::requestAccessToken(const QString& login, const QString& pas QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request; + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); request.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter()); QUrl grantURL = _authURL; @@ -578,6 +579,7 @@ void AccountManager::requestProfile() { profileURL.setPath("/api/v1/user/profile"); QNetworkRequest profileRequest(profileURL); + profileRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); profileRequest.setHeader(QNetworkRequest::UserAgentHeader, _userAgentGetter()); profileRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, _accountInfo.getAccessToken().authorizationHeaderValue()); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index c9c9d73394..b4145c73f5 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -836,6 +836,7 @@ void AddressManager::ifLocalSandboxRunningElse(std::function localSandbo QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest sandboxStatus(SANDBOX_STATUS_URL); + sandboxStatus.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(sandboxStatus); diff --git a/libraries/networking/src/HTTPResourceRequest.cpp b/libraries/networking/src/HTTPResourceRequest.cpp index 11ab436933..392654a419 100644 --- a/libraries/networking/src/HTTPResourceRequest.cpp +++ b/libraries/networking/src/HTTPResourceRequest.cpp @@ -49,6 +49,7 @@ void HTTPResourceRequest::cleanupTimer() { void HTTPResourceRequest::doSend() { QNetworkRequest networkRequest(_url); + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); if (_cacheEnabled) { diff --git a/libraries/networking/src/OAuthNetworkAccessManager.cpp b/libraries/networking/src/OAuthNetworkAccessManager.cpp index 92e7a2ff4f..15d5acbc67 100644 --- a/libraries/networking/src/OAuthNetworkAccessManager.cpp +++ b/libraries/networking/src/OAuthNetworkAccessManager.cpp @@ -37,6 +37,7 @@ QNetworkReply* OAuthNetworkAccessManager::createRequest(QNetworkAccessManager::O if (accountManager->hasValidAccessToken() && req.url().host() == NetworkingConstants::METAVERSE_SERVER_URL.host()) { QNetworkRequest authenticatedRequest(req); + authenticatedRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); authenticatedRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); authenticatedRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, accountManager->getAccountInfo().getAccessToken().authorizationHeaderValue()); diff --git a/libraries/script-engine/src/ScriptsModel.cpp b/libraries/script-engine/src/ScriptsModel.cpp index 0ad2ad01a7..8755195932 100644 --- a/libraries/script-engine/src/ScriptsModel.cpp +++ b/libraries/script-engine/src/ScriptsModel.cpp @@ -185,6 +185,7 @@ void ScriptsModel::requestDefaultFiles(QString marker) { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest request(url); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); QNetworkReply* reply = networkAccessManager.get(request); connect(reply, SIGNAL(finished()), SLOT(downloadFinished())); diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index 15b2576331..818521ecc5 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -45,6 +45,7 @@ XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) : _timer(this), _numRedirects(0) { + _request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); _timer.setSingleShot(true); } diff --git a/tests/gpu-test/src/TestFbx.cpp b/tests/gpu-test/src/TestFbx.cpp index cea356e125..538bb0a973 100644 --- a/tests/gpu-test/src/TestFbx.cpp +++ b/tests/gpu-test/src/TestFbx.cpp @@ -56,6 +56,7 @@ public: explicit FileDownloader(QUrl imageUrl, QObject *parent = 0) : QObject(parent) { connect(&m_WebCtrl, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*))); QNetworkRequest request(imageUrl); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); m_WebCtrl.get(request); } From 98cb3cc366305d961aadb0c9e0c5626abe39957c Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 1 Jul 2016 17:27:00 -0700 Subject: [PATCH 22/40] updated build instructions --- BUILD.md | 12 ++++++------ BUILD_OSX.md | 8 +++----- BUILD_WIN.md | 12 ++++++------ interface/resources/qml/controls/WebView.qml | 4 ---- 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/BUILD.md b/BUILD.md index c868a8e9d9..c51e40cb58 100644 --- a/BUILD.md +++ b/BUILD.md @@ -1,7 +1,7 @@ ###Dependencies * [cmake](http://www.cmake.org/cmake/resources/software.html) ~> 3.3.2 -* [Qt](http://www.qt.io/download-open-source) ~> 5.5.1 +* [Qt](http://www.qt.io/download-open-source) ~> 5.6.1 * [OpenSSL](https://www.openssl.org/community/binaries.html) ~> 1.0.1m * IMPORTANT: Using the recommended version of OpenSSL is critical to avoid security vulnerabilities. * [VHACD](https://github.com/virneo/v-hacd)(clone this repository)(Optional) @@ -41,14 +41,14 @@ If you would like to use a specific install of a dependency instead of the versi Hifi uses CMake to generate build files and project files for your platform. ####Qt -In order for CMake to find the Qt5 find modules, you will need to set an ENV variable pointing to your Qt installation. +In order for CMake to find the Qt5 find modules, you will need to set a QT_CMAKE_PREFIX_PATH environment variable pointing to your Qt installation. -For example, a Qt5 5.5.1 installation to /usr/local/qt5 would require that QT_CMAKE_PREFIX_PATH be set with the following command. This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). +This can either be entered directly into your shell session before you build or in your shell profile (e.g.: ~/.bash_profile, ~/.bashrc, ~/.zshrc - this depends on your shell and environment). The path it needs to be set to will depend on where and how Qt5 was installed. e.g. - export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.5.1/clang_64/lib/cmake/ - export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.5.1/lib/cmake + export QT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.1/clang_64/lib/cmake/ + export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.1-1/lib/cmake export QT_CMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake ####Generating build files @@ -65,7 +65,7 @@ Any variables that need to be set for CMake to find dependencies can be set as E For example, to pass the QT_CMAKE_PREFIX_PATH variable during build file generation: - cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.5.1/lib/cmake + cmake .. -DQT_CMAKE_PREFIX_PATH=/usr/local/qt/5.6.1/lib/cmake ####Finding Dependencies diff --git a/BUILD_OSX.md b/BUILD_OSX.md index 1c9c5a9796..e7b59edcc1 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -4,9 +4,7 @@ Please read the [general build guide](BUILD.md) for information on dependencies [Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all High Fidelity dependencies very simple. brew tap homebrew/versions - brew install cmake openssl qt55 - -We no longer require install of qt5 via our [homebrew formulas repository](https://github.com/highfidelity/homebrew-formulas). Versions of Qt that are 5.5.x provide a mechanism to disable the wireless scanning we previously had a custom patch for. + brew install cmake openssl qt5 ###OpenSSL and Qt @@ -15,9 +13,9 @@ For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR: export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2h_1/ -For Qt 5.5.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows. +For Qt 5.6.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows. - export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt55/5.5.1/lib/cmake + export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.1-1/lib/cmake Note that these use the versions from homebrew formulae at the time of this writing, and the version in the path will likely change. diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 25e20952ca..b8adaad8d1 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -27,17 +27,17 @@ We expect nmake.exe to be located at the following path. ###Qt You can use the online installer or the offline installer. If you use the offline installer, be sure to select the "OpenGL" version. -* [Download the online installer](http://qt-project.org/downloads) +* [Download the online installer](http://www.qt.io/download-open-source/#section-2) * When it asks you to select components, ONLY select one of the following, 32- or 64-bit to match your build preference: - * Qt > Qt 5.5.1 > **msvc2013 32-bit** - * Qt > Qt 5.5.1 > **msvc2013 64-bit** + * Qt > Qt 5.6.1 > **msvc2013 32-bit** + * Qt > Qt 5.6.1 > **msvc2013 64-bit** * Download the offline installer, 32- or 64-bit to match your build preference: - * [32-bit](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013-5.5.1.exe) - * [64-bit](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013_64-5.5.1.exe) + * [32-bit](http://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013-5.6.1-1.exe) + * [64-bit](http://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-windows-x86-msvc2013_64-5.6.1-1.exe) Once Qt is installed, you need to manually configure the following: -* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.5.1\msvc2013\lib\cmake` or `Qt\5.5.1\msvc2013_64\lib\cmake` directory. +* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.6.1\msvc2013\lib\cmake` or `Qt\5.6.1\msvc2013_64\lib\cmake` directory. * You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New ###External Libraries diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 7285db22d2..65031c0035 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -61,8 +61,4 @@ WebEngineView { request.openIn(newWindow.webView); } } - - // This breaks the webchannel used for passing messages. Fixed in Qt 5.6 - // See https://bugreports.qt.io/browse/QTBUG-49521 - //profile: desktop.browserProfile } From b048637c2651143190f3d19f2d9629f22fe05035 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 1 Jul 2016 18:18:35 -0700 Subject: [PATCH 23/40] fix signal issue and qabstract::socket issue --- libraries/shared/src/RegisteredMetaTypes.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 8e6c1ef6ed..171b58de17 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "RegisteredMetaTypes.h" @@ -32,6 +33,7 @@ int xColorMetaTypeId = qRegisterMetaType(); int pickRayMetaTypeId = qRegisterMetaType(); int collisionMetaTypeId = qRegisterMetaType(); int qMapURLStringMetaTypeId = qRegisterMetaType>(); +int socketErrorMetaTypeId = qRegisterMetaType(); void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, mat4toScriptValue, mat4FromScriptValue); From 9903e464d8f54c734a3b3b3b684ed297d1d81902 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 5 Jul 2016 12:54:25 -0700 Subject: [PATCH 24/40] fix error in FileDialog.qml --- interface/resources/qml/dialogs/FileDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index 6a37886cb3..ff8be580db 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -52,7 +52,7 @@ ModalWindow { // Set from OffscreenUi::getOpenFile() property int options; // <-- FIXME unused - property string iconText: text !== "" ? hifi.glyphs.scriptUpload : "" + property string iconText: root.title !== "" ? hifi.glyphs.scriptUpload : "" property int iconSize: 40 property bool selectDirectory: false; From 716647037eefeee04d9a13da1989c0dd994e44a8 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 5 Jul 2016 13:26:25 -0700 Subject: [PATCH 25/40] removed unused code --- interface/src/scripting/WebWindowClass.cpp | 8 ---- interface/src/scripting/WebWindowClass.h | 1 - interface/src/ui/DataWebPage.cpp | 54 ---------------------- interface/src/ui/DataWebPage.h | 26 ----------- 4 files changed, 89 deletions(-) delete mode 100644 interface/src/ui/DataWebPage.cpp delete mode 100644 interface/src/ui/DataWebPage.h diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index 400c538461..99de804b7c 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -51,8 +51,6 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid layout->addWidget(_webView); - addEventBridgeToWindowObject(); - _windowWidget = dialogWidget; auto style = QStyleFactory::create("fusion"); @@ -68,8 +66,6 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid } connect(this, &WebWindowClass::destroyed, _windowWidget, &QWidget::deleteLater); - //connect(_webView->page()->mainFrame(), &QWebFrame::javaScriptWindowObjectCleared, - // this, &WebWindowClass::addEventBridgeToWindowObject); } WebWindowClass::~WebWindowClass() { @@ -92,10 +88,6 @@ void WebWindowClass::hasClosed() { emit closed(); } -void WebWindowClass::addEventBridgeToWindowObject() { -// _webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge); -} - void WebWindowClass::setVisible(bool visible) { if (visible) { QMetaObject::invokeMethod(_windowWidget, "showNormal", Qt::AutoConnection); diff --git a/interface/src/scripting/WebWindowClass.h b/interface/src/scripting/WebWindowClass.h index fdb434ab7e..65ad9a3dcc 100644 --- a/interface/src/scripting/WebWindowClass.h +++ b/interface/src/scripting/WebWindowClass.h @@ -56,7 +56,6 @@ public slots: void setURL(const QString& url); void raise(); ScriptEventBridge* getEventBridge() const { return _eventBridge; } - void addEventBridgeToWindowObject(); void setTitle(const QString& title); signals: diff --git a/interface/src/ui/DataWebPage.cpp b/interface/src/ui/DataWebPage.cpp deleted file mode 100644 index 7168dfc5d6..0000000000 --- a/interface/src/ui/DataWebPage.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// -// DataWebPage.cpp -// interface/src/ui -// -// Created by Stephen Birarda on 2014-09-22. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include "Application.h" -#include -#include - -#include "DataWebPage.h" - -DataWebPage::DataWebPage(QObject* parent) : QWebEnginePage(parent) -{ - // use an OAuthNetworkAccessManager instead of regular QNetworkAccessManager so our requests are authed -// setNetworkAccessManager(OAuthNetworkAccessManager::getInstance()); - - // give the page an empty stylesheet -// settings()->setUserStyleSheetUrl(QUrl()); -} - -//void DataWebPage::javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID) { -// qDebug() << "JS console message at line" << lineNumber << "from" << sourceID << "-" << message; -//} - -bool DataWebPage::acceptNavigationRequest(const QUrl & url, NavigationType type, bool isMainFrame) { - // Handle hifi:// links and links to files with particular extensions - QString urlString = url.toString(); - if (qApp->canAcceptURL(urlString)) { - if (qApp->acceptURL(urlString)) { - return false; // we handled it, so QWebPage doesn't need to handle it - } - } - - // Make hyperlinks with target="_blank" open in user's Web browser - if (type == NavigationTypeLinkClicked && !isMainFrame) { - qApp->openUrl(url); - return false; // We handled it. - } - - return true; -} - -// -//QString DataWebPage::userAgentForUrl(const QUrl& url) const { -// return HIGH_FIDELITY_USER_AGENT; -//} diff --git a/interface/src/ui/DataWebPage.h b/interface/src/ui/DataWebPage.h deleted file mode 100644 index 55e185ca4f..0000000000 --- a/interface/src/ui/DataWebPage.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// DataWebPage.h -// interface/src/ui -// -// Created by Stephen Birarda on 2014-09-22. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_DataWebPage_h -#define hifi_DataWebPage_h - -#include - -class DataWebPage : public QWebEnginePage { -public: - DataWebPage(QObject* parent = 0); -protected: - //virtual void javaScriptConsoleMessage(const QString & message, int lineNumber, const QString & sourceID) override; - virtual bool acceptNavigationRequest(const QUrl & url, NavigationType type, bool isMainFrame) override; - //virtual QString userAgentForUrl(const QUrl& url) const override; -}; - -#endif // hifi_DataWebPage_h From 88d0aa373709aef89fc2ac1c7d431fe10be633fc Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 5 Jul 2016 13:46:42 -0700 Subject: [PATCH 26/40] removed showButtonDown, which didn't exist --- scripts/system/directory.js | 3 +-- scripts/system/libraries/toolBars.js | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/system/directory.js b/scripts/system/directory.js index 2eae4fa9cf..ac65615a68 100644 --- a/scripts/system/directory.js +++ b/scripts/system/directory.js @@ -73,8 +73,7 @@ var toolBar = (function() { width: toolWidth, height: toolHeight, alpha: 0.9, - visible: true, - showButtonDown: true + visible: true }); toolBar.showTool(browseDirectoryButton, true); diff --git a/scripts/system/libraries/toolBars.js b/scripts/system/libraries/toolBars.js index 5a84bf9027..e49f8c4004 100644 --- a/scripts/system/libraries/toolBars.js +++ b/scripts/system/libraries/toolBars.js @@ -130,8 +130,6 @@ Tool = function(properties, selectable, selected) { // selectable and selected a if (update) { if (selectable) { this.toggle(); - } else if (properties.showButtonDown) { - this.buttonDown(true); } } return true; From 2db02fdde16e2147937aca21fa44add50bd2dfe3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 1 Aug 2016 17:41:47 -0700 Subject: [PATCH 27/40] remove QtWebProcess hack on OSX --- cmake/macros/FixupInterface.cmake | 58 ------------------------------- interface/CMakeLists.txt | 3 -- 2 files changed, 61 deletions(-) delete mode 100644 cmake/macros/FixupInterface.cmake diff --git a/cmake/macros/FixupInterface.cmake b/cmake/macros/FixupInterface.cmake deleted file mode 100644 index 3e5ea7a3e2..0000000000 --- a/cmake/macros/FixupInterface.cmake +++ /dev/null @@ -1,58 +0,0 @@ -# -# FixupInterface.cmake -# cmake/macros -# -# Copyright 2016 High Fidelity, Inc. -# Created by Stephen Birarda on January 6th, 2016 -# -# Distributed under the Apache License, Version 2.0. -# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -# - -macro(fixup_interface) - if (APPLE) - - string(REPLACE " " "\\ " ESCAPED_BUNDLE_NAME ${INTERFACE_BUNDLE_NAME}) - string(REPLACE " " "\\ " ESCAPED_INSTALL_PATH ${INTERFACE_INSTALL_DIR}) - set(_INTERFACE_INSTALL_PATH "${ESCAPED_INSTALL_PATH}/${ESCAPED_BUNDLE_NAME}.app") - - # install QtWebProcess from Qt to the application bundle - # since it is missed by macdeployqt - # https://bugreports.qt.io/browse/QTBUG-35211 - set(LIBEXEC_PATH "${_INTERFACE_INSTALL_PATH}/Contents/libexec") - install( - PROGRAMS "${QT_DIR}/libexec/QtWebProcess" - DESTINATION ${LIBEXEC_PATH} - COMPONENT ${CLIENT_COMPONENT} - ) - - set(QTWEBPROCESS_PATH "\${CMAKE_INSTALL_PREFIX}/${LIBEXEC_PATH}") - - # we also need a qt.conf in the directory of QtWebProcess - install(CODE " - file(WRITE ${QTWEBPROCESS_PATH}/qt.conf - \"[Paths]\nPlugins = ../PlugIns\nImports = ../Resources/qml\nQml2Imports = ../Resources/qml\" - )" - COMPONENT ${CLIENT_COMPONENT} - ) - - find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS "${QT_DIR}/bin" NO_DEFAULT_PATH) - - if (NOT MACDEPLOYQT_COMMAND AND (PRODUCTION_BUILD OR PR_BUILD)) - message(FATAL_ERROR "Could not find macdeployqt at ${QT_DIR}/bin.\ - It is required to produce an relocatable interface application.\ - Check that the environment variable QT_DIR points to your Qt installation.\ - ") - endif () - - install(CODE " - execute_process(COMMAND ${MACDEPLOYQT_COMMAND}\ - \${CMAKE_INSTALL_PREFIX}/${_INTERFACE_INSTALL_PATH}/\ - -verbose=2 -qmldir=${CMAKE_SOURCE_DIR}/interface/resources/qml/\ - -executable=\${CMAKE_INSTALL_PREFIX}/${_INTERFACE_INSTALL_PATH}/Contents/libexec/QtWebProcess\ - )" - COMPONENT ${CLIENT_COMPONENT} - ) - - endif () -endmacro() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index b6e9153f65..9ee7c73f71 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -280,9 +280,6 @@ if (APPLE) $/../Resources/scripts ) - # call the fixup_interface macro to add required bundling commands for installation - fixup_interface() - else (APPLE) # copy the resources files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD From c993a6b818c81da2c764d698b9c6f77130a8badb Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 2 Aug 2016 11:23:49 -0700 Subject: [PATCH 28/40] try removing windows hack --- interface/CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 9ee7c73f71..863595c174 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -245,12 +245,6 @@ target_link_libraries( Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets ) -# Issue causes build failure unless we add this directory. -# See https://bugreports.qt.io/browse/QTBUG-43351 -if (WIN32) - add_paths_to_fixup_libs(${Qt5_DIR}/../../../plugins/qtwebengine) -endif() - if (UNIX) target_link_libraries(${TARGET_NAME} pthread) endif(UNIX) From 038aba20e454c3f0b6933fc3f567a5989c67fe47 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 2 Aug 2016 12:14:25 -0700 Subject: [PATCH 29/40] disable dropshadows if unsupported (OSX and AMD cards) --- interface/resources/qml/hifi/Card.qml | 2 ++ interface/resources/qml/windows/DefaultFrameDecoration.qml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/Card.qml b/interface/resources/qml/hifi/Card.qml index 53829eed9e..5d8cbc97fe 100644 --- a/interface/resources/qml/hifi/Card.qml +++ b/interface/resources/qml/hifi/Card.qml @@ -89,6 +89,7 @@ Rectangle { property int dropSamples: 9; property int dropSpread: 0; DropShadow { + visible: desktop.gradientsSupported; source: place; anchors.fill: place; horizontalOffset: dropHorizontalOffset; @@ -99,6 +100,7 @@ Rectangle { spread: dropSpread; } DropShadow { + visible: desktop.gradientsSupported; source: users; anchors.fill: users; horizontalOffset: dropHorizontalOffset; diff --git a/interface/resources/qml/windows/DefaultFrameDecoration.qml b/interface/resources/qml/windows/DefaultFrameDecoration.qml index 40e32aaa6b..1ddd83976e 100644 --- a/interface/resources/qml/windows/DefaultFrameDecoration.qml +++ b/interface/resources/qml/windows/DefaultFrameDecoration.qml @@ -109,7 +109,7 @@ Decoration { verticalOffset: 2 samples: 2 color: hifi.colors.baseGrayShadow60 - visible: (window && window.focus) + visible: (desktop.gradientsSupported && window && window.focus) cached: true } } From 7f2476f0f82ecd9d7deb1de41e460187ba59f16f Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 4 Aug 2016 10:25:52 -0700 Subject: [PATCH 30/40] put pack macdeployqt call, but still removed qtwebprocess --- cmake/macros/FixupInterface.cmake | 37 +++++++++++++++++++++++++++++++ interface/CMakeLists.txt | 3 +++ 2 files changed, 40 insertions(+) create mode 100644 cmake/macros/FixupInterface.cmake diff --git a/cmake/macros/FixupInterface.cmake b/cmake/macros/FixupInterface.cmake new file mode 100644 index 0000000000..93b5cc25fb --- /dev/null +++ b/cmake/macros/FixupInterface.cmake @@ -0,0 +1,37 @@ +# +# FixupInterface.cmake +# cmake/macros +# +# Copyright 2016 High Fidelity, Inc. +# Created by Stephen Birarda on January 6th, 2016 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# + +macro(fixup_interface) + if (APPLE) + + string(REPLACE " " "\\ " ESCAPED_BUNDLE_NAME ${INTERFACE_BUNDLE_NAME}) + string(REPLACE " " "\\ " ESCAPED_INSTALL_PATH ${INTERFACE_INSTALL_DIR}) + set(_INTERFACE_INSTALL_PATH "${ESCAPED_INSTALL_PATH}/${ESCAPED_BUNDLE_NAME}.app") + + find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS "${QT_DIR}/bin" NO_DEFAULT_PATH) + + if (NOT MACDEPLOYQT_COMMAND AND (PRODUCTION_BUILD OR PR_BUILD)) + message(FATAL_ERROR "Could not find macdeployqt at ${QT_DIR}/bin.\ + It is required to produce an relocatable interface application.\ + Check that the environment variable QT_DIR points to your Qt installation.\ + ") + endif () + + install(CODE " + execute_process(COMMAND ${MACDEPLOYQT_COMMAND}\ + \${CMAKE_INSTALL_PREFIX}/${_INTERFACE_INSTALL_PATH}/\ + -verbose=2 -qmldir=${CMAKE_SOURCE_DIR}/interface/resources/qml/\ + )" + COMPONENT ${CLIENT_COMPONENT} + ) + + endif () +endmacro() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 863595c174..f712c4cc21 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -274,6 +274,9 @@ if (APPLE) $/../Resources/scripts ) + # call the fixup_interface macro to add required bundling commands for installation + fixup_interface() + else (APPLE) # copy the resources files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD From 99176588cc6afaa64efb190b4f638eeb84d962fc Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 10 Aug 2016 14:47:38 -0700 Subject: [PATCH 31/40] remove old webwindowclass that relied on qwebkit --- interface/src/Application.cpp | 2 - interface/src/scripting/WebWindowClass.cpp | 173 ------------------ interface/src/scripting/WebWindowClass.h | 79 -------- .../scripting/WindowScriptingInterface.cpp | 5 - .../src/scripting/WindowScriptingInterface.h | 6 - 5 files changed, 265 deletions(-) delete mode 100644 interface/src/scripting/WebWindowClass.cpp delete mode 100644 interface/src/scripting/WebWindowClass.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c22e04a2e4..8620b384ec 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -133,7 +133,6 @@ #include "scripting/LocationScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" -#include "scripting/WebWindowClass.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/ControllerScriptingInterface.h" #include "scripting/ToolbarScriptingInterface.h" @@ -4858,7 +4857,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, LocationScriptingInterface::locationSetter); - scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1); scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor); scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor); diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp deleted file mode 100644 index 99de804b7c..0000000000 --- a/interface/src/scripting/WebWindowClass.cpp +++ /dev/null @@ -1,173 +0,0 @@ -// -// WebWindowClass.cpp -// interface/src/scripting -// -// Created by Ryan Huffman on 11/06/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -#include -#include -#include -#include -#include -#include -#include - -#include "Application.h" -#include "ui/DataWebPage.h" -#include "MainWindow.h" -#include "WebWindowClass.h" -#include "WindowScriptingInterface.h" - -ScriptEventBridge::ScriptEventBridge(QObject* parent) : QObject(parent) { -} - -void ScriptEventBridge::emitWebEvent(const QString& data) { - emit webEventReceived(data); -} - -void ScriptEventBridge::emitScriptEvent(const QString& data) { - emit scriptEventReceived(data); -} - -WebWindowClass::WebWindowClass(const QString& title, const QString& url, int width, int height) - : QObject(NULL), _eventBridge(new ScriptEventBridge(this)) { - auto dialogWidget = new QDialog(qApp->getWindow(), Qt::Window); - dialogWidget->setWindowTitle(title); - dialogWidget->resize(width, height); - dialogWidget->installEventFilter(this); - connect(dialogWidget, &QDialog::finished, this, &WebWindowClass::hasClosed); - - auto layout = new QVBoxLayout(dialogWidget); - layout->setContentsMargins(0, 0, 0, 0); - dialogWidget->setLayout(layout); - - _webView = new QWebEngineView(dialogWidget); - - layout->addWidget(_webView); - - _windowWidget = dialogWidget; - - auto style = QStyleFactory::create("fusion"); - if (style) { - _webView->setStyle(style); - } - - _webView->setPage(new DataWebPage()); - if (!url.startsWith("http") && !url.startsWith("file://")) { - _webView->setUrl(QUrl::fromLocalFile(url)); - } else { - _webView->setUrl(url); - } - - connect(this, &WebWindowClass::destroyed, _windowWidget, &QWidget::deleteLater); -} - -WebWindowClass::~WebWindowClass() { -} - -bool WebWindowClass::eventFilter(QObject* sender, QEvent* event) { - if (sender == _windowWidget) { - if (event->type() == QEvent::Move) { - emit moved(getPosition()); - } - if (event->type() == QEvent::Resize) { - emit resized(getSize()); - } - } - - return false; -} - -void WebWindowClass::hasClosed() { - emit closed(); -} - -void WebWindowClass::setVisible(bool visible) { - if (visible) { - QMetaObject::invokeMethod(_windowWidget, "showNormal", Qt::AutoConnection); - QMetaObject::invokeMethod(_windowWidget, "raise", Qt::AutoConnection); - } - QMetaObject::invokeMethod(_windowWidget, "setVisible", Qt::AutoConnection, Q_ARG(bool, visible)); -} - -void WebWindowClass::setURL(const QString& url) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setURL", Qt::AutoConnection, Q_ARG(QString, url)); - return; - } - _webView->setUrl(url); -} - -QSizeF WebWindowClass::getSize() const { - QSizeF size = _windowWidget->size(); - return size; -} - -void WebWindowClass::setSize(QSizeF size) { - setSize(size.width(), size.height()); -} - -void WebWindowClass::setSize(int width, int height) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setSize", Qt::AutoConnection, Q_ARG(int, width), Q_ARG(int, height)); - return; - } - _windowWidget->resize(width, height); -} - -glm::vec2 WebWindowClass::getPosition() const { - QPoint position = _windowWidget->pos(); - return glm::vec2(position.x(), position.y()); -} - -void WebWindowClass::setPosition(glm::vec2 position) { - setPosition(position.x, position.y); -} - -void WebWindowClass::setPosition(int x, int y) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setPosition", Qt::AutoConnection, Q_ARG(int, x), Q_ARG(int, y)); - return; - } - _windowWidget->move(x, y); -} - -void WebWindowClass::raise() { - QMetaObject::invokeMethod(_windowWidget, "showNormal", Qt::AutoConnection); - QMetaObject::invokeMethod(_windowWidget, "raise", Qt::AutoConnection); -} - -QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { - WebWindowClass* retVal; - QString file = context->argument(0).toString(); - if (context->argument(4).toBool()) { - qWarning() << "ToolWindow views with WebWindow are no longer supported. Use OverlayWebWindow instead"; - return QScriptValue(); - } else { - qWarning() << "WebWindow views are deprecated. Use OverlayWebWindow instead"; - } - QMetaObject::invokeMethod(DependencyManager::get().data(), "doCreateWebWindow", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(WebWindowClass*, retVal), - Q_ARG(const QString&, file), - Q_ARG(QString, context->argument(1).toString()), - Q_ARG(int, context->argument(2).toInteger()), - Q_ARG(int, context->argument(3).toInteger())); - - connect(engine, &QScriptEngine::destroyed, retVal, &WebWindowClass::deleteLater); - - return engine->newQObject(retVal); -} - -void WebWindowClass::setTitle(const QString& title) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setTitle", Qt::AutoConnection, Q_ARG(QString, title)); - return; - } - _windowWidget->setWindowTitle(title); -} diff --git a/interface/src/scripting/WebWindowClass.h b/interface/src/scripting/WebWindowClass.h deleted file mode 100644 index 65ad9a3dcc..0000000000 --- a/interface/src/scripting/WebWindowClass.h +++ /dev/null @@ -1,79 +0,0 @@ -// -// WebWindowClass.h -// interface/src/scripting -// -// Created by Ryan Huffman on 11/06/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_WebWindowClass_h -#define hifi_WebWindowClass_h - -#include -#include -#include - -class ScriptEventBridge : public QObject { - Q_OBJECT -public: - ScriptEventBridge(QObject* parent = NULL); - -public slots: - void emitWebEvent(const QString& data); - void emitScriptEvent(const QString& data); - -signals: - void webEventReceived(const QString& data); - void scriptEventReceived(const QString& data); - -}; - -class WebWindowClass : public QObject { - Q_OBJECT - Q_PROPERTY(QObject* eventBridge READ getEventBridge) - Q_PROPERTY(QString url READ getURL) - Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition); - Q_PROPERTY(QSizeF size READ getSize WRITE setSize); - -public: - WebWindowClass(const QString& title, const QString& url, int width, int height); - ~WebWindowClass(); - - static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); - -public slots: - void setVisible(bool visible); - glm::vec2 getPosition() const; - void setPosition(int x, int y); - void setPosition(glm::vec2 position); - QSizeF getSize() const; - void setSize(QSizeF size); - void setSize(int width, int height); - QString getURL() const { return _webView->url().url(); } - void setURL(const QString& url); - void raise(); - ScriptEventBridge* getEventBridge() const { return _eventBridge; } - void setTitle(const QString& title); - -signals: - void visibilityChanged(bool visible); // Tool window - void moved(glm::vec2 position); - void resized(QSizeF size); - void closed(); - -protected: - virtual bool eventFilter(QObject* sender, QEvent* event) override; - -private slots: - void hasClosed(); - -private: - QWidget* _windowWidget; - QWebEngineView* _webView; - ScriptEventBridge* _eventBridge; -}; - -#endif diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 4eb8c67250..c528c26b99 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -21,7 +21,6 @@ #include "MainWindow.h" #include "Menu.h" #include "OffscreenUi.h" -#include "WebWindowClass.h" #include "WindowScriptingInterface.h" @@ -61,10 +60,6 @@ WindowScriptingInterface::WindowScriptingInterface() { }); } -WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height) { - return new WebWindowClass(title, url, width, height); -} - QScriptValue WindowScriptingInterface::hasFocus() { return qApp->hasFocus(); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 7a01be7fac..715d0657a3 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -16,9 +16,6 @@ #include #include -class WebWindowClass; - - class CustomPromptResult { public: QVariant value; @@ -65,9 +62,6 @@ signals: void snapshotTaken(const QString& path, bool notify); void snapshotShared(const QString& error); -private slots: - WebWindowClass* doCreateWebWindow(const QString& title, const QString& url, int width, int height); - private: QString getPreviousBrowseLocation() const; void setPreviousBrowseLocation(const QString& location); From 8a4d48ca8d7ab55b104a0371ded85ce082581915 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 10 Aug 2016 15:16:02 -0700 Subject: [PATCH 32/40] set browserProfile now that Qt bug is fixed --- interface/resources/qml/Browser.qml | 12 ++++++------ interface/resources/qml/controls/WebView.qml | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index 050e10eead..62226859b6 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -223,12 +223,12 @@ ScrollingWindow { var newWindow = component.createObject(desktop); request.openIn(newWindow.webView) } - Component.onCompleted: { - desktop.initWebviewProfileHandlers(webview.profile) - } - - //profile: desktop.browserProfile + Component.onCompleted: { + desktop.initWebviewProfileHandlers(webview.profile) + } + + profile: desktop.browserProfile } } // item @@ -245,4 +245,4 @@ ScrollingWindow { break; } } -} // dialog \ No newline at end of file +} // dialog diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 65031c0035..84ef31e87f 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -5,7 +5,7 @@ WebEngineView { id: root property var newUrl; - profile.httpUserAgent: "Mozilla/5.0 Chrome (HighFidelityInterface)" + profile: desktop.browserProfile Component.onCompleted: { console.log("Connecting JS messaging to Hifi Logging") @@ -13,7 +13,6 @@ WebEngineView { root.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) { console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message); }); - } // FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6 From 7e0ad52cd5f71739a903719b9f6fdd18a73be913 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 10 Aug 2016 15:31:54 -0700 Subject: [PATCH 33/40] updated build instructions to use official qt release instead of homebrew --- BUILD_OSX.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/BUILD_OSX.md b/BUILD_OSX.md index e7b59edcc1..55d4276aa0 100644 --- a/BUILD_OSX.md +++ b/BUILD_OSX.md @@ -1,23 +1,31 @@ Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only OS X specific instructions are found in this file. ###Homebrew -[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of all High Fidelity dependencies very simple. +[Homebrew](http://brew.sh/) is an excellent package manager for OS X. It makes install of some High Fidelity dependencies very simple. brew tap homebrew/versions - brew install cmake openssl qt5 + brew install cmake openssl -###OpenSSL and Qt +###OpenSSL -Assuming you've installed OpenSSL or Qt 5 using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR and QT_CMAKE_PREFIX_PATH so CMake can find your installations. +Assuming you've installed OpenSSL using the homebrew instructions above, you'll need to set OPENSSL_ROOT_DIR so CMake can find your installations. For OpenSSL installed via homebrew, set OPENSSL_ROOT_DIR: export OPENSSL_ROOT_DIR=/usr/local/Cellar/openssl/1.0.2h_1/ - -For Qt 5.6.1 installed via homebrew, set QT_CMAKE_PREFIX_PATH as follows. - export QT_CMAKE_PREFIX_PATH=/usr/local/Cellar/qt5/5.6.1-1/lib/cmake +Note that this uses the version from the homebrew formula at the time of this writing, and the version in the path will likely change. -Note that these use the versions from homebrew formulae at the time of this writing, and the version in the path will likely change. +###Qt +You can use the online installer or the offline installer. + +* [Download the online installer](http://www.qt.io/download-open-source/#section-2) + * When it asks you to select components, select the following: + * Qt > Qt 5.6 + +* [Download the offline installer](http://download.qt.io/official_releases/qt/5.6/5.6.1-1/qt-opensource-mac-x64-clang-5.6.1-1.dmg) + +Once Qt is installed, you need to manually configure the following: +* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt5.6.1/5.6/clang_64/lib/cmake/` directory. ###Xcode If Xcode is your editor of choice, you can ask CMake to generate Xcode project files instead of Unix Makefiles. From 74e5814a48d8c16eb86f73e64c843913e1b6a4b6 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 12 Aug 2016 13:35:57 -0700 Subject: [PATCH 34/40] try to fix rpath issue --- assignment-client/CMakeLists.txt | 5 +++++ domain-server/CMakeLists.txt | 5 +++++ interface/CMakeLists.txt | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index d0fd2c1176..773ef845e2 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -1,5 +1,10 @@ set(TARGET_NAME assignment-client) +# Fix up the rpath so macdeployqt works +if (APPLE) + set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") +endif () + setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets) # link in the shared libraries diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index b7eb8c0138..8a14907da3 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -6,6 +6,11 @@ else () set(_SHOULD_SYMLINK_RESOURCES FALSE) endif () +# Fix up the rpath so macdeployqt works +if (APPLE) + set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") +endif () + # setup the project and link required Qt modules setup_hifi_project(Network) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index f712c4cc21..f999a1a8f2 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -123,7 +123,8 @@ if (APPLE) add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS} ${QM}) # make sure the output name for the .app bundle is correct - set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME ${INTERFACE_BUNDLE_NAME}) + # Fix up the rpath so macdeployqt works + set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") elseif (WIN32) # configure an rc file for the chosen icon set(CONFIGURE_ICON_PATH "${CMAKE_CURRENT_SOURCE_DIR}/icon/${INTERFACE_ICON_FILENAME}") From 544606bcccc953a2026dce0f2182bab5aab1f425 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 12 Aug 2016 13:46:22 -0700 Subject: [PATCH 35/40] cmake fix --- assignment-client/CMakeLists.txt | 4 ++-- domain-server/CMakeLists.txt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index 773ef845e2..54afabfd21 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -1,12 +1,12 @@ set(TARGET_NAME assignment-client) +setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets) + # Fix up the rpath so macdeployqt works if (APPLE) set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") endif () -setup_hifi_project(Core Gui Network Script Quick Widgets WebSockets) - # link in the shared libraries link_hifi_libraries( audio avatars octree gpu model fbx entities diff --git a/domain-server/CMakeLists.txt b/domain-server/CMakeLists.txt index 8a14907da3..746e599d4e 100644 --- a/domain-server/CMakeLists.txt +++ b/domain-server/CMakeLists.txt @@ -6,14 +6,14 @@ else () set(_SHOULD_SYMLINK_RESOURCES FALSE) endif () +# setup the project and link required Qt modules +setup_hifi_project(Network) + # Fix up the rpath so macdeployqt works if (APPLE) set_target_properties(${TARGET_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") endif () -# setup the project and link required Qt modules -setup_hifi_project(Network) - # TODO: find a solution that will handle web file changes in resources on windows without a re-build. # Currently the resources are only copied on post-build. If one is changed but the domain-server is not, they will # not be re-copied. This is worked-around on OS X/UNIX by using a symlink. From b8da976123b96d13183547f06f75ed4b45172d80 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Fri, 12 Aug 2016 14:44:46 -0700 Subject: [PATCH 36/40] added Info.plist to Components.app so that macdeployqt from Qt 5.6 doesn't throw a segfault --- cmake/macros/InstallBesideConsole.cmake | 6 ++++ ...MacOSXBundleSandboxComponentsInfo.plist.in | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 cmake/templates/MacOSXBundleSandboxComponentsInfo.plist.in diff --git a/cmake/macros/InstallBesideConsole.cmake b/cmake/macros/InstallBesideConsole.cmake index 0eb6025f38..0e1cb3237e 100644 --- a/cmake/macros/InstallBesideConsole.cmake +++ b/cmake/macros/InstallBesideConsole.cmake @@ -59,6 +59,12 @@ macro(install_beside_console) set(EXECUTABLE_NEEDING_FIXUP "\${CMAKE_INSTALL_PREFIX}/${COMPONENT_INSTALL_DIR}/${TARGET_NAME}") string(REPLACE " " "\\ " ESCAPED_EXECUTABLE_NAME ${EXECUTABLE_NEEDING_FIXUP}) + # configure an rc file for the chosen icon + set(MACOSX_BUNDLE_EXECUTABLE_NAME "domain-server") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.highfidelity.server-components") + set(MACOSX_BUNDLE_BUNDLE_NAME "Sandbox Components") + configure_file("${HF_CMAKE_DIR}/templates/MacOSXBundleSandboxComponentsInfo.plist.in" "${COMPONENTS_BUNDLE_PATH}/Contents/Info.plist") + install(CODE " execute_process(COMMAND ${MACDEPLOYQT_COMMAND} ${ESCAPED_BUNDLE_NAME} -verbose=2 -executable=${ESCAPED_EXECUTABLE_NAME})" COMPONENT ${SERVER_COMPONENT} diff --git a/cmake/templates/MacOSXBundleSandboxComponentsInfo.plist.in b/cmake/templates/MacOSXBundleSandboxComponentsInfo.plist.in new file mode 100644 index 0000000000..a466dc7c4e --- /dev/null +++ b/cmake/templates/MacOSXBundleSandboxComponentsInfo.plist.in @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + LSRequiresCarbon + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + + From 930afad49c6caa3d678cfda6c706d4c5e0c0c287 Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Fri, 12 Aug 2016 14:48:58 -0700 Subject: [PATCH 37/40] comment fix --- cmake/macros/InstallBesideConsole.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/macros/InstallBesideConsole.cmake b/cmake/macros/InstallBesideConsole.cmake index 0e1cb3237e..979cc0b412 100644 --- a/cmake/macros/InstallBesideConsole.cmake +++ b/cmake/macros/InstallBesideConsole.cmake @@ -59,7 +59,7 @@ macro(install_beside_console) set(EXECUTABLE_NEEDING_FIXUP "\${CMAKE_INSTALL_PREFIX}/${COMPONENT_INSTALL_DIR}/${TARGET_NAME}") string(REPLACE " " "\\ " ESCAPED_EXECUTABLE_NAME ${EXECUTABLE_NEEDING_FIXUP}) - # configure an rc file for the chosen icon + # configure Info.plist for COMPONENT_APP set(MACOSX_BUNDLE_EXECUTABLE_NAME "domain-server") set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.highfidelity.server-components") set(MACOSX_BUNDLE_BUNDLE_NAME "Sandbox Components") From 62edaf0ec9ae81b0f78d8781a58111814ef50346 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 12 Aug 2016 18:37:05 -0700 Subject: [PATCH 38/40] commit for marko to look at later --- cmake/macros/InstallBesideConsole.cmake | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cmake/macros/InstallBesideConsole.cmake b/cmake/macros/InstallBesideConsole.cmake index 979cc0b412..449fa7631c 100644 --- a/cmake/macros/InstallBesideConsole.cmake +++ b/cmake/macros/InstallBesideConsole.cmake @@ -60,12 +60,11 @@ macro(install_beside_console) string(REPLACE " " "\\ " ESCAPED_EXECUTABLE_NAME ${EXECUTABLE_NEEDING_FIXUP}) # configure Info.plist for COMPONENT_APP - set(MACOSX_BUNDLE_EXECUTABLE_NAME "domain-server") - set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.highfidelity.server-components") - set(MACOSX_BUNDLE_BUNDLE_NAME "Sandbox Components") - configure_file("${HF_CMAKE_DIR}/templates/MacOSXBundleSandboxComponentsInfo.plist.in" "${COMPONENTS_BUNDLE_PATH}/Contents/Info.plist") - install(CODE " + set(MACOSX_BUNDLE_EXECUTABLE_NAME 'domain-server') + set(MACOSX_BUNDLE_GUI_IDENTIFIER 'com.highfidelity.server-components') + set(MACOSX_BUNDLE_BUNDLE_NAME 'Sandbox Components') + configure_file('${HF_CMAKE_DIR}/templates/MacOSXBundleSandboxComponentsInfo.plist.in' '${COMPONENTS_BUNDLE_PATH}/Contents/Info.plist') execute_process(COMMAND ${MACDEPLOYQT_COMMAND} ${ESCAPED_BUNDLE_NAME} -verbose=2 -executable=${ESCAPED_EXECUTABLE_NAME})" COMPONENT ${SERVER_COMPONENT} ) From da5f7912b54d243bc506f3624aef7a49393f118f Mon Sep 17 00:00:00 2001 From: Marko Kudjerski Date: Fri, 19 Aug 2016 16:53:38 -0700 Subject: [PATCH 39/40] fixed Info.plist setup for Components.app --- cmake/macros/InstallBesideConsole.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/macros/InstallBesideConsole.cmake b/cmake/macros/InstallBesideConsole.cmake index 449fa7631c..d5777fff12 100644 --- a/cmake/macros/InstallBesideConsole.cmake +++ b/cmake/macros/InstallBesideConsole.cmake @@ -61,10 +61,10 @@ macro(install_beside_console) # configure Info.plist for COMPONENT_APP install(CODE " - set(MACOSX_BUNDLE_EXECUTABLE_NAME 'domain-server') - set(MACOSX_BUNDLE_GUI_IDENTIFIER 'com.highfidelity.server-components') - set(MACOSX_BUNDLE_BUNDLE_NAME 'Sandbox Components') - configure_file('${HF_CMAKE_DIR}/templates/MacOSXBundleSandboxComponentsInfo.plist.in' '${COMPONENTS_BUNDLE_PATH}/Contents/Info.plist') + set(MACOSX_BUNDLE_EXECUTABLE_NAME domain-server) + set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.server-components) + set(MACOSX_BUNDLE_BUNDLE_NAME Sandbox\\ Components) + configure_file(${HF_CMAKE_DIR}/templates/MacOSXBundleSandboxComponentsInfo.plist.in ${ESCAPED_BUNDLE_NAME}/Contents/Info.plist) execute_process(COMMAND ${MACDEPLOYQT_COMMAND} ${ESCAPED_BUNDLE_NAME} -verbose=2 -executable=${ESCAPED_EXECUTABLE_NAME})" COMPONENT ${SERVER_COMPONENT} ) From 9d057233c6fe09f01ee3f19979feef53080d9b51 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 23 Aug 2016 19:19:15 -0700 Subject: [PATCH 40/40] Fixing rebase --- interface/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index f999a1a8f2..3e8b55431d 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -243,7 +243,7 @@ target_link_libraries( ${TARGET_NAME} Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg - Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets + Qt5::WebChannel Qt5::WebEngine ) if (UNIX)