diff --git a/libs/juce-current/patches/22_mingw-filechooser-no-vista.patch b/libs/juce-current/patches/22_mingw-filechooser-no-vista.patch index 6849d29d..8194204d 100644 --- a/libs/juce-current/patches/22_mingw-filechooser-no-vista.patch +++ b/libs/juce-current/patches/22_mingw-filechooser-no-vista.patch @@ -1,16 +1,16 @@ -diff --git a/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp b/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp -index 0c1138a2b..a5b42b8ef 100644 ---- a/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp -+++ b/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp -@@ -172,6 +172,7 @@ private: +diff --git a/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp b/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp +index 0c1138a2b..a5b42b8ef 100644 +--- a/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp ++++ b/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp +@@ -172,6 +172,7 @@ private: void operator() (LPWSTR ptr) const noexcept { CoTaskMemFree (ptr); } }; + #if JUCE_MSVC - bool showDialog (IFileDialog& dialog, bool async) const + bool showDialog (IFileDialog& dialog, bool async) { FILEOPENDIALOGOPTIONS flags = {}; -@@ -327,6 +328,7 @@ private: +@@ -327,6 +328,7 @@ private: return result; } @@ -18,7 +18,7 @@ index 0c1138a2b..a5b42b8ef 100644 Array openDialogPreVista (bool async) { -@@ -436,11 +438,13 @@ private: +@@ -436,11 +438,13 @@ private: const Remover remover (*this); diff --git a/libs/juce-current/patches/24_vital-needed-changes.patch b/libs/juce-current/patches/24_vital-needed-changes.patch index a53c614b..dd909c1d 100644 --- a/libs/juce-current/patches/24_vital-needed-changes.patch +++ b/libs/juce-current/patches/24_vital-needed-changes.patch @@ -1,8 +1,8 @@ -diff --git a/modules/juce_gui_basics/components/juce_Component.cpp b/modules/juce_gui_basics/components/juce_Component.cpp -index c4404c637..fa8d3ccf5 100644 ---- a/modules/juce_gui_basics/components/juce_Component.cpp -+++ b/modules/juce_gui_basics/components/juce_Component.cpp -@@ -387,6 +387,10 @@ struct Component::ComponentHelpers +diff --git a/modules/juce_gui_basics/components/juce_Component.cpp b/modules/juce_gui_basics/components/juce_Component.cpp +index c4404c637..fa8d3ccf5 100644 +--- a/modules/juce_gui_basics/components/juce_Component.cpp ++++ b/modules/juce_gui_basics/components/juce_Component.cpp +@@ -387,6 +387,10 @@ struct Component::ComponentHelpers template static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p) { @@ -13,7 +13,7 @@ index c4404c637..fa8d3ccf5 100644 while (source != nullptr) { if (source == target) -@@ -395,6 +399,9 @@ struct Component::ComponentHelpers +@@ -395,6 +399,9 @@ struct Component::ComponentHelpers if (source->isParentOf (target)) return convertFromDistantParentSpace (source, *target, p); @@ -23,7 +23,7 @@ index c4404c637..fa8d3ccf5 100644 p = convertToParentSpace (*source, p); source = source->getParentComponent(); } -@@ -1390,13 +1397,14 @@ bool Component::reallyContains (Point point, bool returnTrueIfWithinAChild) +@@ -1390,13 +1397,14 @@ bool Component::reallyContains (Point point, bool returnTrueIfWithinAChild) Component* Component::getComponentAt (Point position) { @@ -39,11 +39,11 @@ index c4404c637..fa8d3ccf5 100644 if (child != nullptr) return child; -diff --git a/modules/juce_gui_basics/components/juce_Component.h b/modules/juce_gui_basics/components/juce_Component.h -index 6b2b0072b..ccb2681fa 100644 ---- a/modules/juce_gui_basics/components/juce_Component.h -+++ b/modules/juce_gui_basics/components/juce_Component.h -@@ -2284,6 +2284,17 @@ public: +diff --git a/modules/juce_gui_basics/components/juce_Component.h b/modules/juce_gui_basics/components/juce_Component.h +index 6b2b0072b..ccb2681fa 100644 +--- a/modules/juce_gui_basics/components/juce_Component.h ++++ b/modules/juce_gui_basics/components/juce_Component.h +@@ -2284,6 +2284,17 @@ public: */ bool getViewportIgnoreDragFlag() const noexcept { return flags.viewportIgnoreDragFlag; } @@ -61,11 +61,11 @@ index 6b2b0072b..ccb2681fa 100644 private: //============================================================================== friend class ComponentPeer; -diff --git a/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp b/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp -index a8c2c283a..ddb15b88d 100644 ---- a/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp -+++ b/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp -@@ -61,7 +61,7 @@ public: +diff --git a/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp b/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp +index a8c2c283a..ddb15b88d 100644 +--- a/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp ++++ b/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp +@@ -61,7 +61,7 @@ public: { if (auto* peer = comp.getPeer()) { @@ -74,11 +74,11 @@ index a8c2c283a..ddb15b88d 100644 auto& peerComp = peer->getComponent(); return comp.getLocalPoint (&peerComp, ScalingHelpers::unscaledScreenPosToScaled (peerComp, pos)); } -diff --git a/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp b/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp -index 8d7febd4b..7ec8fbb00 100644 ---- a/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp -+++ b/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp -@@ -474,7 +474,7 @@ bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info) +diff --git a/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp b/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp +index 8d7febd4b..7ec8fbb00 100644 +--- a/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp ++++ b/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp +@@ -474,7 +474,7 @@ bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info) if (DragHelpers::isSuitableTarget (info, newTarget)) { dragAndDropTargetComponent = newTarget; @@ -87,7 +87,7 @@ index 8d7febd4b..7ec8fbb00 100644 if (DragHelpers::isFileDrag (info)) dynamic_cast (newTarget)->fileDragEnter (info.files, pos.x, pos.y); -@@ -491,7 +491,7 @@ bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info) +@@ -491,7 +491,7 @@ bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info) if (! DragHelpers::isSuitableTarget (info, newTarget)) return false; @@ -96,11 +96,11 @@ index 8d7febd4b..7ec8fbb00 100644 if (DragHelpers::isFileDrag (info)) dynamic_cast (newTarget)->fileDragMove (info.files, pos.x, pos.y); -diff --git a/modules/juce_opengl/native/juce_OpenGLExtensions.h b/modules/juce_opengl/native/juce_OpenGLExtensions.h -index e7eab9dbf..d7039b144 100644 ---- a/modules/juce_opengl/native/juce_OpenGLExtensions.h -+++ b/modules/juce_opengl/native/juce_OpenGLExtensions.h -@@ -83,7 +83,13 @@ namespace juce +diff --git a/modules/juce_opengl/native/juce_OpenGLExtensions.h b/modules/juce_opengl/native/juce_OpenGLExtensions.h +index e7eab9dbf..d7039b144 100644 +--- a/modules/juce_opengl/native/juce_OpenGLExtensions.h ++++ b/modules/juce_opengl/native/juce_OpenGLExtensions.h +@@ -83,7 +83,13 @@ namespace juce USE_FUNCTION (glCheckFramebufferStatus, GLenum, (GLenum p1), (p1))\ USE_FUNCTION (glFramebufferTexture2D, void, (GLenum p1, GLenum p2, GLenum p3, GLuint p4, GLint p5), (p1, p2, p3, p4, p5))\ USE_FUNCTION (glFramebufferRenderbuffer, void, (GLenum p1, GLenum p2, GLenum p3, GLuint p4), (p1, p2, p3, p4))\ diff --git a/libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp b/libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp index 8d9ad2cd..de83ae85 100644 --- a/libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp +++ b/libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp @@ -42,7 +42,7 @@ void MidiKeyboardState::reset() bool MidiKeyboardState::isNoteOn (const int midiChannel, const int n) const noexcept { - jassert (midiChannel >= 0 && midiChannel <= 16); + jassert (midiChannel > 0 && midiChannel <= 16); return isPositiveAndBelow (n, 128) && (noteStates[n] & (1 << (midiChannel - 1))) != 0; @@ -56,7 +56,7 @@ bool MidiKeyboardState::isNoteOnForChannels (const int midiChannelMask, const in void MidiKeyboardState::noteOn (const int midiChannel, const int midiNoteNumber, const float velocity) { - jassert (midiChannel >= 0 && midiChannel <= 16); + jassert (midiChannel > 0 && midiChannel <= 16); jassert (isPositiveAndBelow (midiNoteNumber, 128)); const ScopedLock sl (lock); diff --git a/libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp b/libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp index f1c3d24f..24a6c1f8 100644 --- a/libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp +++ b/libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp @@ -36,7 +36,7 @@ bool MidiRPNDetector::parseControllerMessage (int midiChannel, int controllerValue, MidiRPNMessage& result) noexcept { - jassert (midiChannel >= 1 && midiChannel <= 16); + jassert (midiChannel > 0 && midiChannel <= 16); jassert (controllerNumber >= 0 && controllerNumber < 128); jassert (controllerValue >= 0 && controllerValue < 128); diff --git a/libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp b/libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp index 74b3c144..a8a75a92 100644 --- a/libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp +++ b/libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp @@ -97,7 +97,7 @@ void MPEChannelAssigner::noteOff (int noteNumber, int midiChannel) return false; }; - if (midiChannel >= 0 && midiChannel < 17) + if (midiChannel >= 0 && midiChannel <= 16) { removeNote (midiChannels[midiChannel], noteNumber); return; diff --git a/libs/juce-current/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/libs/juce-current/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 7642040b..ed02448b 100644 --- a/libs/juce-current/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/libs/juce-current/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -842,6 +842,26 @@ namespace WavFileHelpers return out.getMemoryBlock(); } }; + + //============================================================================== + struct Clm_Chunk + { + static MemoryBlock createFrom (const StringPairArray& values) + { + MemoryOutputStream out; + auto s = values["clm "]; + + if (s.isNotEmpty()) + { + out.writeString (s); + + if ((out.getDataSize() & 1) != 0) + out.writeByte(0); + } + + return out.getMemoryBlock(); + } + }; //============================================================================== namespace AXMLChunk @@ -1321,6 +1341,7 @@ public: listInfoChunk = ListInfoChunk::createFrom (metadataValues); acidChunk = AcidChunk::createFrom (metadataValues); trckChunk = TracktionChunk::createFrom (metadataValues); + clm_Chunk = Clm_Chunk::createFrom (metadataValues); } headerPosition = out->getPosition(); @@ -1383,7 +1404,7 @@ public: } private: - MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, listInfoChunk, acidChunk, trckChunk; + MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk, listInfoChunk, acidChunk, trckChunk, clm_Chunk; uint64 lengthInSamples = 0, bytesWritten = 0; int64 headerPosition = 0; bool writeFailed = false; @@ -1421,6 +1442,7 @@ private: + chunkSize (listInfoChunk) + chunkSize (acidChunk) + chunkSize (trckChunk) + + chunkSize (clm_Chunk) + (8 + 28)); // (ds64 chunk) riffChunkSize += (riffChunkSize & 1); @@ -1503,6 +1525,7 @@ private: writeChunk (listInfoChunk, chunkName ("LIST")); writeChunk (acidChunk, chunkName ("acid")); writeChunk (trckChunk, chunkName ("Trkn")); + writeChunk (clm_Chunk, chunkName ("clm ")); writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); diff --git a/libs/juce-current/source/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm b/libs/juce-current/source/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm index 1a687907..aad5da8f 100644 --- a/libs/juce-current/source/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm +++ b/libs/juce-current/source/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm @@ -1755,6 +1755,9 @@ private: Array parameterGroups; //============================================================================== + // According to the docs, this is the maximum size of a MIDIPacketList. + static constexpr UInt32 packetListBytes = 65536; + AudioUnitEvent auEvent; mutable Array presetsArray; CriticalSection incomingMidiLock; @@ -1762,6 +1765,7 @@ private: AudioTimeStamp lastTimeStamp; int totalInChannels, totalOutChannels; HeapBlock pulledSucceeded; + HeapBlock packetList { packetListBytes, 1 }; ThreadLocalValue inParameterChangedCallback; @@ -1858,37 +1862,55 @@ private: void pushMidiOutput (UInt32 nFrames) noexcept { - UInt32 numPackets = 0; - size_t dataSize = 0; + MIDIPacket* end = nullptr; - for (const auto metadata : midiEvents) + const auto init = [&] { - jassert (isPositiveAndBelow (metadata.samplePosition, nFrames)); - ignoreUnused (nFrames); + end = MIDIPacketListInit (packetList); + }; - dataSize += (size_t) metadata.numBytes; - ++numPackets; - } - - MIDIPacket* p; - const size_t packetMembersSize = sizeof (MIDIPacket) - sizeof (p->data); // NB: GCC chokes on "sizeof (MidiMessage::data)" - const size_t packetListMembersSize = sizeof (MIDIPacketList) - sizeof (p->data); + const auto send = [&] + { + midiCallback.midiOutputCallback (midiCallback.userData, &lastTimeStamp, 0, packetList); + }; - HeapBlock packetList; - packetList.malloc (packetListMembersSize + packetMembersSize * numPackets + dataSize, 1); - packetList->numPackets = numPackets; + const auto add = [&] (const MidiMessageMetadata& metadata) + { + end = MIDIPacketListAdd (packetList, + packetListBytes, + end, + static_cast (metadata.samplePosition), + static_cast (metadata.numBytes), + metadata.data); + }; - p = packetList->packet; + init(); for (const auto metadata : midiEvents) { - p->timeStamp = (MIDITimeStamp) metadata.samplePosition; - p->length = (UInt16) metadata.numBytes; - memcpy (p->data, metadata.data, (size_t) metadata.numBytes); - p = MIDIPacketNext (p); + jassert (isPositiveAndBelow (metadata.samplePosition, nFrames)); + ignoreUnused (nFrames); + + add (metadata); + + if (end == nullptr) + { + send(); + init(); + add (metadata); + + if (end == nullptr) + { + // If this is hit, the size of this midi packet exceeds the maximum size of + // a MIDIPacketList. Large SysEx messages should be broken up into smaller + // chunks. + jassertfalse; + init(); + } + } } - midiCallback.midiOutputCallback (midiCallback.userData, &lastTimeStamp, 0, packetList); + send(); } void GetAudioBufferList (bool isInput, int busIdx, AudioBufferList*& bufferList, bool& interleaved, int& numChannels) diff --git a/libs/juce-current/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/libs/juce-current/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp index f722ab36..4747eb0d 100644 --- a/libs/juce-current/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp +++ b/libs/juce-current/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp @@ -1158,11 +1158,6 @@ public: { auto editorBounds = getSizeToContainChild(); - #if JUCE_MAC - if (wrapper.useNSView) - setTopLeftPosition (0, getHeight() - editorBounds.getHeight()); - #endif - resizeHostWindow (editorBounds.getWidth(), editorBounds.getHeight()); { diff --git a/libs/juce-current/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/libs/juce-current/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index 7909098c..19eb385f 100644 --- a/libs/juce-current/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/libs/juce-current/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -28,6 +28,9 @@ //============================================================================== #if JucePlugin_Build_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX) +#if JUCE_LINUX^M + #include ^M +#endif^M #if JUCE_PLUGINHOST_VST3 #if JUCE_MAC @@ -2618,6 +2621,9 @@ public: //============================================================================== void processParameterChanges (Vst::IParameterChanges& paramChanges) { + if (juceVST3EditController == nullptr) + return; + jassert (pluginInstance != nullptr); auto numParamsChanged = paramChanges.getParameterCount(); diff --git a/libs/juce-current/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/libs/juce-current/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index d9359b73..acb80f93 100644 --- a/libs/juce-current/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/libs/juce-current/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -78,9 +78,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4355) //============================================================================== namespace juce { -#if JUCE_WINDOWS - extern void setThreadDPIAwarenessForWindow (HWND); -#endif //============================================================================== namespace @@ -2848,38 +2845,30 @@ public: if (recursiveResize) return; - auto* topComp = getTopLevelComponent(); - - if (topComp->getPeer() != nullptr) + if (auto* peer = getTopLevelComponent()->getPeer()) { - auto pos = (topComp->getLocalPoint (this, Point()) * nativeScaleFactor).roundToInt(); + const ScopedValueSetter recursiveResizeSetter (recursiveResize, true); - recursiveResize = true; + auto pos = (peer->getAreaCoveredBy (*this).toFloat() * nativeScaleFactor).toNearestInt(); #if JUCE_WINDOWS if (pluginHWND != 0) { - setThreadDPIAwarenessForWindow (pluginHWND); - - MoveWindow (pluginHWND, pos.getX(), pos.getY(), - roundToInt (getWidth() * nativeScaleFactor), - roundToInt (getHeight() * nativeScaleFactor), - TRUE); + ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { pluginHWND }; + MoveWindow (pluginHWND, pos.getX(), pos.getY(), pos.getWidth(), pos.getHeight(), TRUE); } #elif JUCE_LINUX if (pluginWindow != 0) { X11Symbols::getInstance()->xMoveResizeWindow (display, pluginWindow, pos.getX(), pos.getY(), - static_cast (roundToInt ((float) getWidth() * nativeScaleFactor)), - static_cast (roundToInt ((float) getHeight() * nativeScaleFactor))); + (unsigned int) pos.getWidth(), + (unsigned int) pos.getHeight()); X11Symbols::getInstance()->xMapRaised (display, pluginWindow); X11Symbols::getInstance()->xFlush (display); } #endif - - recursiveResize = false; } } @@ -3107,7 +3096,12 @@ private: JUCE_END_IGNORE_WARNINGS_MSVC RECT r; - GetWindowRect (pluginHWND, &r); + + { + ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { pluginHWND }; + GetWindowRect (pluginHWND, &r); + } + auto w = (int) (r.right - r.left); auto h = (int) (r.bottom - r.top); @@ -3122,7 +3116,7 @@ private: // very dodgy logic to decide which size is right. if (std::abs (rw - w) > 350 || std::abs (rh - h) > 350) { - setThreadDPIAwarenessForWindow (pluginHWND); + ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { pluginHWND }; SetWindowPos (pluginHWND, 0, 0, 0, roundToInt (rw * nativeScaleFactor), roundToInt (rh * nativeScaleFactor), diff --git a/libs/juce-current/source/modules/juce_audio_processors/juce_audio_processors.cpp b/libs/juce-current/source/modules/juce_audio_processors/juce_audio_processors.cpp index 14915480..fd0e191e 100644 --- a/libs/juce-current/source/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/libs/juce-current/source/modules/juce_audio_processors/juce_audio_processors.cpp @@ -35,6 +35,7 @@ #define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 #define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 #define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1 +#define JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER 1 #include "juce_audio_processors.h" #include diff --git a/libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp b/libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp index 670cb396..ae0874fc 100644 --- a/libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp +++ b/libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp @@ -54,38 +54,24 @@ void AudioProcessorEditor::hostMIDIControllerIsAvailable (bool) { void AudioProcessorEditor::initialise() { - resizable = false; - - attachConstrainer (&defaultConstrainer); + setConstrainer (&defaultConstrainer); resizeListener.reset (new AudioProcessorEditorListener (*this)); addComponentListener (resizeListener.get()); } //============================================================================== -void AudioProcessorEditor::setResizable (const bool shouldBeResizable, const bool useBottomRightCornerResizer) +void AudioProcessorEditor::setResizable (bool allowHostToResize, bool useBottomRightCornerResizer) { - if (shouldBeResizable != resizable) - { - resizable = shouldBeResizable; - - if (! resizable && constrainer == &defaultConstrainer) - { - auto width = getWidth(); - auto height = getHeight(); - - if (width > 0 && height > 0) - defaultConstrainer.setSizeLimits (width, height, width, height); - } - } + resizableByHost = allowHostToResize; - bool shouldHaveCornerResizer = (useBottomRightCornerResizer && shouldBeResizable); + const auto hasResizableCorner = (resizableCorner.get() != nullptr); - if (shouldHaveCornerResizer != (resizableCorner != nullptr)) + if (useBottomRightCornerResizer != hasResizableCorner) { - if (shouldHaveCornerResizer) + if (useBottomRightCornerResizer) attachResizableCornerComponent(); else - resizableCorner.reset(); + resizableCorner = nullptr; } } @@ -94,19 +80,23 @@ void AudioProcessorEditor::setResizeLimits (int newMinimumWidth, int newMaximumWidth, int newMaximumHeight) noexcept { - // if you've set up a custom constrainer then these settings won't have any effect.. - jassert (constrainer == &defaultConstrainer || constrainer == nullptr); + if (constrainer != nullptr && constrainer != &defaultConstrainer) + { + // if you've set up a custom constrainer then these settings won't have any effect.. + jassertfalse; + return; + } - const bool shouldEnableResize = (newMinimumWidth != newMaximumWidth || newMinimumHeight != newMaximumHeight); - const bool shouldHaveCornerResizer = (shouldEnableResize != resizable || resizableCorner != nullptr); + resizableByHost = (newMinimumWidth != newMaximumWidth || newMinimumHeight != newMaximumHeight); - setResizable (shouldEnableResize, shouldHaveCornerResizer); + defaultConstrainer.setSizeLimits (newMinimumWidth, newMinimumHeight, + newMaximumWidth, newMaximumHeight); if (constrainer == nullptr) setConstrainer (&defaultConstrainer); - defaultConstrainer.setSizeLimits (newMinimumWidth, newMinimumHeight, - newMaximumWidth, newMaximumHeight); + if (resizableCorner != nullptr) + attachResizableCornerComponent(); setBoundsConstrained (getBounds()); } @@ -115,29 +105,21 @@ void AudioProcessorEditor::setConstrainer (ComponentBoundsConstrainer* newConstr { if (constrainer != newConstrainer) { - if (newConstrainer != nullptr) - resizable = (newConstrainer->getMinimumWidth() != newConstrainer->getMaximumWidth() - || newConstrainer->getMinimumHeight() != newConstrainer->getMaximumHeight()); + constrainer = newConstrainer; + updatePeer(); - attachConstrainer (newConstrainer); + if (constrainer != nullptr) + resizableByHost = (newConstrainer->getMinimumWidth() != newConstrainer->getMaximumWidth() + || newConstrainer->getMinimumHeight() != newConstrainer->getMaximumHeight()); if (resizableCorner != nullptr) attachResizableCornerComponent(); } } -void AudioProcessorEditor::attachConstrainer (ComponentBoundsConstrainer* newConstrainer) -{ - if (constrainer != newConstrainer) - { - constrainer = newConstrainer; - updatePeer(); - } -} - void AudioProcessorEditor::attachResizableCornerComponent() { - resizableCorner.reset (new ResizableCornerComponent (this, constrainer)); + resizableCorner = std::make_unique (this, constrainer); Component::addChildComponent (resizableCorner.get()); resizableCorner->setAlwaysOnTop (true); editorResized (true); @@ -175,11 +157,6 @@ void AudioProcessorEditor::editorResized (bool wasResized) getHeight() - resizerSize, resizerSize, resizerSize); } - - if (! resizable) - if (auto w = getWidth()) - if (auto h = getHeight()) - defaultConstrainer.setSizeLimits (w, h, w, h); } } diff --git a/libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h b/libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h index 07d3765f..a472f39e 100644 --- a/libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h +++ b/libs/juce-current/source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h @@ -54,12 +54,12 @@ public: /** Destructor. */ ~AudioProcessorEditor() override; - //============================================================================== /** The AudioProcessor that this editor represents. */ AudioProcessor& processor; /** Returns a pointer to the processor that this editor represents. + This method is here to support legacy code, but it's easier to just use the AudioProcessorEditor::processor member variable directly to get this object. */ @@ -76,6 +76,7 @@ public: /** Some types of plugin can call this to suggest that the control for a particular parameter should be highlighted. + Currently only AAX plugins will call this, and implementing it is optional. */ virtual void setControlHighlight (ParameterControlHighlightInfo); @@ -117,36 +118,45 @@ public: virtual void setScaleFactor (float newScale); //============================================================================== - /** Marks the host's editor window as resizable - - @param allowHostToResize whether the editor's parent window can be resized - by the user or the host. Even if this is false, you - can still resize your window yourself by calling - setBounds (for example, when a user clicks on a button - in your editor to drop out a panel) which will bypass any - resizable/constraints checks. If you are using - your own corner resizer than this will also bypass - any checks. - @param useBottomRightCornerResizer + /** Sets whether the editor is resizable by the host and/or user. + + @param allowHostToResize whether the editor's parent window can be resized + by the host. Even if this is false, you can still + resize your window yourself by calling setBounds + (for example, when a user clicks on a button in + your editor to drop out a panel) which will bypass + any resizable/constraints checks. + @param useBottomRightCornerResizer if this is true, a ResizableCornerComponent will be + added to the editor's bottom-right to allow the user + to resize the editor regardless of the value of + `allowHostToResize`. + @see setResizeLimits, isResizable */ void setResizable (bool allowHostToResize, bool useBottomRightCornerResizer); - /** Returns true if the host is allowed to resize editor's parent window + /** Returns true if the host is allowed to resize the editor's parent window. @see setResizable */ - bool isResizable() const noexcept { return resizable; } + bool isResizable() const noexcept { return resizableByHost; } /** This sets the maximum and minimum sizes for the window. If the window's current size is outside these limits, it will be resized to make sure it's within them. + If you pass in a different minimum and maximum size, this will mark the editor + as resizable by the host. + A direct call to setBounds() will bypass any constraint checks, but when the window is dragged by the user or resized by other indirect means, the constrainer will limit the numbers involved. + Note that if you have set a custom constrainer for this editor then this will have + no effect, and if you have removed the constrainer with `setConstrainer (nullptr);` + then this will re-add the default constrainer with the new limits. + @see setResizable */ void setResizeLimits (int newMinimumWidth, @@ -154,8 +164,8 @@ public: int newMaximumWidth, int newMaximumHeight) noexcept; - /** Returns the bounds constrainer object that this window is using. + You can access this to change its properties. */ ComponentBoundsConstrainer* getConstrainer() noexcept { return constrainer; } @@ -176,11 +186,14 @@ public: */ void setBoundsConstrained (Rectangle newBounds); + /** The ResizableCornerComponent which is currently being used by this editor, + or nullptr if it does not have one. + */ std::unique_ptr resizableCorner; private: //============================================================================== - struct AudioProcessorEditorListener : ComponentListener + struct AudioProcessorEditorListener : public ComponentListener { AudioProcessorEditorListener (AudioProcessorEditor& e) : ed (e) {} @@ -198,14 +211,13 @@ private: void initialise(); void editorResized (bool wasResized); void updatePeer(); - void attachConstrainer (ComponentBoundsConstrainer*); void attachResizableCornerComponent(); //============================================================================== std::unique_ptr resizeListener; - bool resizable; + bool resizableByHost = false; ComponentBoundsConstrainer defaultConstrainer; - ComponentBoundsConstrainer* constrainer = {}; + ComponentBoundsConstrainer* constrainer = nullptr; AffineTransform hostScaleTransform; JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor) diff --git a/libs/juce-current/source/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp b/libs/juce-current/source/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp index 50cae68e..549d8edb 100644 --- a/libs/juce-current/source/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp +++ b/libs/juce-current/source/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp @@ -393,6 +393,7 @@ public: AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, TRANS("Error when trying to open audio device!"), error); + resized(); } bool showDeviceControlPanel() @@ -1120,6 +1121,8 @@ void AudioDeviceSelectorComponent::updateMidiOutput() deviceManager.setDefaultMidiOutputDevice ({}); else deviceManager.setDefaultMidiOutputDevice (currentMidiOutputs[selectedId - 1].identifier); + + resized(); } void AudioDeviceSelectorComponent::changeListenerCallback (ChangeBroadcaster*) diff --git a/libs/juce-current/source/modules/juce_core/native/juce_curl_Network.cpp b/libs/juce-current/source/modules/juce_core/native/juce_curl_Network.cpp index 00d42835..dd3375ff 100644 --- a/libs/juce-current/source/modules/juce_core/native/juce_curl_Network.cpp +++ b/libs/juce-current/source/modules/juce_core/native/juce_curl_Network.cpp @@ -332,6 +332,9 @@ public: // or 3) data is in the in buffer while ((! finished) && curlBuffer.getSize() == 0) { + if (Thread::currentThreadShouldExit()) + return false; + { const ScopedLock lock (cleanupLock); diff --git a/libs/juce-current/source/modules/juce_core/native/juce_linux_Files.cpp b/libs/juce-current/source/modules/juce_core/native/juce_linux_Files.cpp index 3dc4602c..72046b93 100644 --- a/libs/juce-current/source/modules/juce_core/native/juce_linux_Files.cpp +++ b/libs/juce-current/source/modules/juce_core/native/juce_linux_Files.cpp @@ -203,7 +203,7 @@ bool Process::openDocument (const String& fileName, const String& parameters) for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", "google-chrome", "chromium-browser", "opera", "konqueror" }) { - cmdLines.add (String (browserName) + " " + cmdString.trim()); + cmdLines.add (String (browserName) + " " + cmdString.trim().quoted()); } cmdString = cmdLines.joinIntoString (" || "); diff --git a/libs/juce-current/source/modules/juce_core/system/juce_PlatformDefs.h b/libs/juce-current/source/modules/juce_core/system/juce_PlatformDefs.h index 0a1798ba..bd4f5d99 100644 --- a/libs/juce-current/source/modules/juce_core/system/juce_PlatformDefs.h +++ b/libs/juce-current/source/modules/juce_core/system/juce_PlatformDefs.h @@ -186,7 +186,8 @@ namespace juce #define JUCE_STRINGIFY(item) JUCE_STRINGIFY_MACRO_HELPER (item) //============================================================================== -/** This is a shorthand macro for declaring stubs for a class's copy constructor and operator=. +/** This is a shorthand macro for deleting a class's copy constructor and + copy assignment operator. For example, instead of @code @@ -214,6 +215,13 @@ namespace juce className (const className&) = delete;\ className& operator= (const className&) = delete; +/** This is a shorthand macro for deleting a class's move constructor and + move assignment operator. +*/ +#define JUCE_DECLARE_NON_MOVEABLE(className) \ + className (className&&) = delete;\ + className& operator= (className&&) = delete; + /** This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for a class. */ diff --git a/libs/juce-current/source/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h b/libs/juce-current/source/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h index 7f64d433..e9360e0d 100644 --- a/libs/juce-current/source/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h +++ b/libs/juce-current/source/modules/juce_dsp/native/juce_sse_SIMDNativeOps.h @@ -74,6 +74,7 @@ struct SIMDNativeOps static forcedinline __m128 JUCE_VECTOR_CALLTYPE add (__m128 a, __m128 b) noexcept { return _mm_add_ps (a, b); } static forcedinline __m128 JUCE_VECTOR_CALLTYPE sub (__m128 a, __m128 b) noexcept { return _mm_sub_ps (a, b); } static forcedinline __m128 JUCE_VECTOR_CALLTYPE mul (__m128 a, __m128 b) noexcept { return _mm_mul_ps (a, b); } + static forcedinline __m128 JUCE_VECTOR_CALLTYPE div (__m128 a, __m128 b) noexcept { return _mm_div_ps (a, b); } static forcedinline __m128 JUCE_VECTOR_CALLTYPE bit_and (__m128 a, __m128 b) noexcept { return _mm_and_ps (a, b); } static forcedinline __m128 JUCE_VECTOR_CALLTYPE bit_or (__m128 a, __m128 b) noexcept { return _mm_or_ps (a, b); } static forcedinline __m128 JUCE_VECTOR_CALLTYPE bit_xor (__m128 a, __m128 b) noexcept { return _mm_xor_ps (a, b); } @@ -142,6 +143,7 @@ struct SIMDNativeOps static forcedinline __m128d JUCE_VECTOR_CALLTYPE add (__m128d a, __m128d b) noexcept { return _mm_add_pd (a, b); } static forcedinline __m128d JUCE_VECTOR_CALLTYPE sub (__m128d a, __m128d b) noexcept { return _mm_sub_pd (a, b); } static forcedinline __m128d JUCE_VECTOR_CALLTYPE mul (__m128d a, __m128d b) noexcept { return _mm_mul_pd (a, b); } + static forcedinline __m128d JUCE_VECTOR_CALLTYPE div (__m128d a, __m128d b) noexcept { return _mm_div_pd (a, b); } static forcedinline __m128d JUCE_VECTOR_CALLTYPE bit_and (__m128d a, __m128d b) noexcept { return _mm_and_pd (a, b); } static forcedinline __m128d JUCE_VECTOR_CALLTYPE bit_or (__m128d a, __m128d b) noexcept { return _mm_or_pd (a, b); } static forcedinline __m128d JUCE_VECTOR_CALLTYPE bit_xor (__m128d a, __m128d b) noexcept { return _mm_xor_pd (a, b); } diff --git a/libs/juce-current/source/modules/juce_events/native/juce_mac_MessageManager.mm b/libs/juce-current/source/modules/juce_events/native/juce_mac_MessageManager.mm index 1cc0b9f8..9d83519f 100644 --- a/libs/juce-current/source/modules/juce_events/native/juce_mac_MessageManager.mm +++ b/libs/juce-current/source/modules/juce_events/native/juce_mac_MessageManager.mm @@ -33,300 +33,302 @@ using MenuTrackingChangedCallback = void (*)(bool); MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr; //============================================================================== -struct AppDelegate +struct AppDelegateClass : public ObjCClass { -public: - AppDelegate() + AppDelegateClass() : ObjCClass ("JUCEAppDelegate_") { - static AppDelegateClass cls; - delegate = [cls.createInstance() init]; + addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@"); + addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@"); + addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@"); + addMethod (@selector (application:openFile:), application_openFile, "c@:@@"); + addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@"); + addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@"); + addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@"); + addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@"); - NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@"); + addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@"); + addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@"); + addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@"); + addMethod (@selector (dummyMethod), dummyMethod, "v@:"); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + #if JUCE_PUSH_NOTIFICATIONS + //============================================================================== + addIvar*> ("pushNotificationsDelegate"); + + addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching, "v@:@"); JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") - [center addObserver: delegate selector: @selector (mainMenuTrackingBegan:) - name: NSMenuDidBeginTrackingNotification object: nil]; - [center addObserver: delegate selector: @selector (mainMenuTrackingEnded:) - name: NSMenuDidEndTrackingNotification object: nil]; + addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate, "v@:@"); JUCE_END_IGNORE_WARNINGS_GCC_LIKE - if (JUCEApplicationBase::isStandaloneApp()) - { - [NSApp setDelegate: delegate]; + addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications, "v@:@@"); + addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications, "v@:@@"); + addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification, "v@:@@"); + #endif - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") - [[NSDistributedNotificationCenter defaultCenter] addObserver: delegate - selector: @selector (broadcastMessageCallback:) - name: getBroadcastEventName() - object: nil - suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately]; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - } - else - { - [center addObserver: delegate selector: @selector (applicationDidResignActive:) - name: NSApplicationDidResignActiveNotification object: NSApp]; + registerClass(); + } - [center addObserver: delegate selector: @selector (applicationDidBecomeActive:) - name: NSApplicationDidBecomeActiveNotification object: NSApp]; +private: + static void applicationWillFinishLaunching (id self, SEL, NSNotification*) + { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + [[NSAppleEventManager sharedAppleEventManager] setEventHandler: self + andSelector: @selector (getUrl:withReplyEvent:) + forEventClass: kInternetEventClass + andEventID: kAEGetURL]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } - [center addObserver: delegate selector: @selector (applicationWillUnhide:) - name: NSApplicationWillUnhideNotification object: NSApp]; + #if JUCE_PUSH_NOTIFICATIONS + static void applicationDidFinishLaunching (id self, SEL, NSNotification* notification) + { + if (notification.userInfo != nil) + { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + // NSUserNotification is deprecated from macOS 11, but there doesn't seem to be a + // replacement for NSApplicationLaunchUserNotificationKey returning a non-deprecated type + NSUserNotification* userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + + if (userNotification != nil && userNotification.userInfo != nil) + didReceiveRemoteNotification (self, nil, [NSApplication sharedApplication], userNotification.userInfo); } } + #endif - ~AppDelegate() + static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*) { - [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: delegate]; - [[NSNotificationCenter defaultCenter] removeObserver: delegate]; - - if (JUCEApplicationBase::isStandaloneApp()) + if (auto* app = JUCEApplicationBase::getInstance()) { - [NSApp setDelegate: nil]; + app->systemRequestedQuit(); - [[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate - name: getBroadcastEventName() - object: nil]; + if (! MessageManager::getInstance()->hasStopMessageBeenSent()) + return NSTerminateCancel; } - [delegate release]; + return NSTerminateNow; } - static NSString* getBroadcastEventName() + static void applicationWillTerminate (id /*self*/, SEL, NSNotification*) { - return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64())); + JUCEApplicationBase::appWillTerminateByForce(); } - MessageQueue messageQueue; - id delegate; - -private: - //============================================================================== - struct AppDelegateClass : public ObjCClass + static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename) { - AppDelegateClass() : ObjCClass ("JUCEAppDelegate_") + if (auto* app = JUCEApplicationBase::getInstance()) { - addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@"); - addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@"); - addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@"); - addMethod (@selector (application:openFile:), application_openFile, "c@:@@"); - addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@"); - addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@"); - addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@"); - addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@"); + app->anotherInstanceStarted (quotedIfContainsSpaces (filename)); + return YES; + } - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") - addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@"); - addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@"); - addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@"); - addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@"); - addMethod (@selector (dummyMethod), dummyMethod, "v@:"); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + return NO; + } - #if JUCE_PUSH_NOTIFICATIONS - //============================================================================== - addIvar*> ("pushNotificationsDelegate"); + static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames) + { + if (auto* app = JUCEApplicationBase::getInstance()) + { + StringArray files; - addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching, "v@:@"); + for (NSString* f in filenames) + files.add (quotedIfContainsSpaces (f)); - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") - addMethod (@selector (setPushNotificationsDelegate:), setPushNotificationsDelegate, "v@:@"); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + if (files.size() > 0) + app->anotherInstanceStarted (files.joinIntoString (" ")); + } + } - addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), registeredForRemoteNotifications, "v@:@@"); - addMethod (@selector (application:didFailToRegisterForRemoteNotificationsWithError:), failedToRegisterForRemoteNotifications, "v@:@@"); - addMethod (@selector (application:didReceiveRemoteNotification:), didReceiveRemoteNotification, "v@:@@"); - #endif + static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } + static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } + static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); } - registerClass(); - } + static void broadcastMessageCallback (id /*self*/, SEL, NSNotification* n) + { + NSDictionary* dict = (NSDictionary*) [n userInfo]; + auto messageString = nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")]); + MessageManager::getInstance()->deliverBroadcastMessage (messageString); + } - private: - static void applicationWillFinishLaunching (id self, SEL, NSNotification*) - { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") - [[NSAppleEventManager sharedAppleEventManager] setEventHandler: self - andSelector: @selector (getUrl:withReplyEvent:) - forEventClass: kInternetEventClass - andEventID: kAEGetURL]; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - } + static void mainMenuTrackingBegan (id /*self*/, SEL, NSNotification*) + { + if (menuTrackingChangedCallback != nullptr) + (*menuTrackingChangedCallback) (true); + } - #if JUCE_PUSH_NOTIFICATIONS - static void applicationDidFinishLaunching (id self, SEL, NSNotification* notification) - { - if (notification.userInfo != nil) - { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") - // NSUserNotification is deprecated from macOS 11, but there doesn't seem to be a - // replacement for NSApplicationLaunchUserNotificationKey returning a non-deprecated type - NSUserNotification* userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey]; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - - if (userNotification != nil && userNotification.userInfo != nil) - didReceiveRemoteNotification (self, nil, [NSApplication sharedApplication], userNotification.userInfo); - } - } - #endif + static void mainMenuTrackingEnded (id /*self*/, SEL, NSNotification*) + { + if (menuTrackingChangedCallback != nullptr) + (*menuTrackingChangedCallback) (false); + } - static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*) - { - if (auto* app = JUCEApplicationBase::getInstance()) - { - app->systemRequestedQuit(); + static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread) - if (! MessageManager::getInstance()->hasStopMessageBeenSent()) - return NSTerminateCancel; - } + static void focusChanged() + { + if (appFocusChangeCallback != nullptr) + (*appFocusChangeCallback)(); + } - return NSTerminateNow; - } + static void getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*) + { + if (auto* app = JUCEApplicationBase::getInstance()) + app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue])); + } - static void applicationWillTerminate (id /*self*/, SEL, NSNotification*) - { - JUCEApplicationBase::appWillTerminateByForce(); - } + static String quotedIfContainsSpaces (NSString* file) + { + String s (nsStringToJuce (file)); + s = s.unquoted().replace ("\"", "\\\""); - static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename) - { - if (auto* app = JUCEApplicationBase::getInstance()) - { - app->anotherInstanceStarted (quotedIfContainsSpaces (filename)); - return YES; - } + if (s.containsChar (' ')) + s = s.quoted(); - return NO; - } + return s; + } - static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames) - { - if (auto* app = JUCEApplicationBase::getInstance()) - { - StringArray files; + #if JUCE_PUSH_NOTIFICATIONS + //============================================================================== + static void setPushNotificationsDelegate (id self, SEL, NSObject* delegate) + { + object_setInstanceVariable (self, "pushNotificationsDelegate", delegate); + } - for (NSString* f in filenames) - files.add (quotedIfContainsSpaces (f)); + static NSObject* getPushNotificationsDelegate (id self) + { + return getIvar*> (self, "pushNotificationsDelegate"); + } - if (files.size() > 0) - app->anotherInstanceStarted (files.joinIntoString (" ")); - } - } + static void registeredForRemoteNotifications (id self, SEL, NSApplication* application, NSData* deviceToken) + { + auto* delegate = getPushNotificationsDelegate (self); - static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } - static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } - static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); } + SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:); - static void broadcastMessageCallback (id /*self*/, SEL, NSNotification* n) + if (delegate != nil && [delegate respondsToSelector: selector]) { - NSDictionary* dict = (NSDictionary*) [n userInfo]; - auto messageString = nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")]); - MessageManager::getInstance()->deliverBroadcastMessage (messageString); - } + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; + [invocation setSelector: selector]; + [invocation setTarget: delegate]; + [invocation setArgument: &application atIndex:2]; + [invocation setArgument: &deviceToken atIndex:3]; - static void mainMenuTrackingBegan (id /*self*/, SEL, NSNotification*) - { - if (menuTrackingChangedCallback != nullptr) - (*menuTrackingChangedCallback) (true); + [invocation invoke]; } + } - static void mainMenuTrackingEnded (id /*self*/, SEL, NSNotification*) - { - if (menuTrackingChangedCallback != nullptr) - (*menuTrackingChangedCallback) (false); - } + static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication* application, NSError* error) + { + auto* delegate = getPushNotificationsDelegate (self); - static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread) + SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:); - static void focusChanged() + if (delegate != nil && [delegate respondsToSelector: selector]) { - if (appFocusChangeCallback != nullptr) - (*appFocusChangeCallback)(); - } + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; + [invocation setSelector: selector]; + [invocation setTarget: delegate]; + [invocation setArgument: &application atIndex:2]; + [invocation setArgument: &error atIndex:3]; - static void getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*) - { - if (auto* app = JUCEApplicationBase::getInstance()) - app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue])); + [invocation invoke]; } + } - static String quotedIfContainsSpaces (NSString* file) - { - String s (nsStringToJuce (file)); - s = s.unquoted().replace ("\"", "\\\""); + static void didReceiveRemoteNotification (id self, SEL, NSApplication* application, NSDictionary* userInfo) + { + auto* delegate = getPushNotificationsDelegate (self); - if (s.containsChar (' ')) - s = s.quoted(); + SEL selector = @selector (application:didReceiveRemoteNotification:); - return s; - } - - #if JUCE_PUSH_NOTIFICATIONS - //============================================================================== - static void setPushNotificationsDelegate (id self, SEL, NSObject* delegate) + if (delegate != nil && [delegate respondsToSelector: selector]) { - object_setInstanceVariable (self, "pushNotificationsDelegate", delegate); - } + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; + [invocation setSelector: selector]; + [invocation setTarget: delegate]; + [invocation setArgument: &application atIndex:2]; + [invocation setArgument: &userInfo atIndex:3]; - static NSObject* getPushNotificationsDelegate (id self) - { - return getIvar*> (self, "pushNotificationsDelegate"); + [invocation invoke]; } + } + #endif +}; - static void registeredForRemoteNotifications (id self, SEL, NSApplication* application, NSData* deviceToken) - { - auto* delegate = getPushNotificationsDelegate (self); +// This is declared at file scope, so that it's guaranteed to be +// constructed before and destructed after `appDelegate` (below) +static AppDelegateClass appDelegateClass; - SEL selector = @selector (application:didRegisterForRemoteNotificationsWithDeviceToken:); +//============================================================================== +struct AppDelegate +{ +public: + AppDelegate() + { + delegate = [appDelegateClass.createInstance() init]; - if (delegate != nil && [delegate respondsToSelector: selector]) - { - NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; - [invocation setSelector: selector]; - [invocation setTarget: delegate]; - [invocation setArgument: &application atIndex:2]; - [invocation setArgument: &deviceToken atIndex:3]; + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; - [invocation invoke]; - } - } + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + [center addObserver: delegate selector: @selector (mainMenuTrackingBegan:) + name: NSMenuDidBeginTrackingNotification object: nil]; + [center addObserver: delegate selector: @selector (mainMenuTrackingEnded:) + name: NSMenuDidEndTrackingNotification object: nil]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE - static void failedToRegisterForRemoteNotifications (id self, SEL, NSApplication* application, NSError* error) + if (JUCEApplicationBase::isStandaloneApp()) { - auto* delegate = getPushNotificationsDelegate (self); + [NSApp setDelegate: delegate]; - SEL selector = @selector (application:didFailToRegisterForRemoteNotificationsWithError:); + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") + [[NSDistributedNotificationCenter defaultCenter] addObserver: delegate + selector: @selector (broadcastMessageCallback:) + name: getBroadcastEventName() + object: nil + suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately]; + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + } + else + { + [center addObserver: delegate selector: @selector (applicationDidResignActive:) + name: NSApplicationDidResignActiveNotification object: NSApp]; - if (delegate != nil && [delegate respondsToSelector: selector]) - { - NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; - [invocation setSelector: selector]; - [invocation setTarget: delegate]; - [invocation setArgument: &application atIndex:2]; - [invocation setArgument: &error atIndex:3]; + [center addObserver: delegate selector: @selector (applicationDidBecomeActive:) + name: NSApplicationDidBecomeActiveNotification object: NSApp]; - [invocation invoke]; - } + [center addObserver: delegate selector: @selector (applicationWillUnhide:) + name: NSApplicationWillUnhideNotification object: NSApp]; } + } - static void didReceiveRemoteNotification (id self, SEL, NSApplication* application, NSDictionary* userInfo) + ~AppDelegate() + { + [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: delegate]; + [[NSNotificationCenter defaultCenter] removeObserver: delegate]; + + if (JUCEApplicationBase::isStandaloneApp()) { - auto* delegate = getPushNotificationsDelegate (self); + [NSApp setDelegate: nil]; - SEL selector = @selector (application:didReceiveRemoteNotification:); + [[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate + name: getBroadcastEventName() + object: nil]; + } - if (delegate != nil && [delegate respondsToSelector: selector]) - { - NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: [delegate methodSignatureForSelector: selector]]; - [invocation setSelector: selector]; - [invocation setTarget: delegate]; - [invocation setArgument: &application atIndex:2]; - [invocation setArgument: &userInfo atIndex:3]; + [delegate release]; + } - [invocation invoke]; - } - } - #endif - }; + static NSString* getBroadcastEventName() + { + return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64())); + } + + MessageQueue messageQueue; + id delegate; }; //============================================================================== @@ -367,6 +369,7 @@ void MessageManager::runDispatchLoop() static void shutdownNSApp() { [NSApp stop: nil]; + [NSEvent stopPeriodicEvents]; [NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1]; } diff --git a/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_Button.cpp b/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_Button.cpp index 1df2d6e2..e5c243ed 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_Button.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_Button.cpp @@ -482,10 +482,7 @@ void Button::mouseDrag (const MouseEvent& e) bool Button::isMouseSourceOver (const MouseEvent& e) { - if (e.source.isTouch() || e.source.isPen()) - return getLocalBounds().toFloat().contains (e.position); - - return isMouseOver(); + return getLocalBounds().toFloat().contains (e.position); } void Button::focusGained (FocusChangeType) diff --git a/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp b/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp index 31454463..f3b61e2c 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp @@ -76,9 +76,6 @@ void ShapeButton::setShape (const Path& newShape, shape = newShape; maintainShapeProportions = maintainShapeProportions_; - shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, Point())); - setComponentEffect (hasShadow ? &shadow : nullptr); - if (resizeNowToFitThisShape) { auto newBounds = shape.getBounds(); diff --git a/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_ShapeButton.h b/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_ShapeButton.h index 6863b1ca..5f9cee9e 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_ShapeButton.h +++ b/libs/juce-current/source/modules/juce_gui_basics/buttons/juce_ShapeButton.h @@ -88,6 +88,8 @@ public: Colour overColourOn, Colour downColourOn); + void setShadowColour (Colour shadow) { shadowColour = shadow; } + /** Set whether the button should use the 'on' set of colours when its toggle state is 'on'. By default these will be the same as the normal colours but the setOnColours method can be used to provide a different set of colours. @@ -112,9 +114,8 @@ public: private: //============================================================================== Colour normalColour, overColour, downColour, - normalColourOn, overColourOn, downColourOn, outlineColour; + normalColourOn, overColourOn, downColourOn, outlineColour, shadowColour; bool useOnColours; - DropShadowEffect shadow; Path shape; BorderSize border; bool maintainShapeProportions; diff --git a/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.cpp b/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.cpp index fa8d3ccf..b2d75c65 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.cpp @@ -3021,7 +3021,8 @@ void Component::modifierKeysChanged (const ModifierKeys& modifiers) void Component::internalModifierKeysChanged() { - sendFakeMouseMove(); + auto mainMouse = Desktop::getInstance().getMainMouseSource(); + mainMouse.triggerFakeMove(); modifierKeysChanged (ModifierKeys::currentModifiers); } diff --git a/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.cpp.orig b/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.cpp.orig index c4404c63..fa8d3ccf 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.cpp.orig +++ b/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.cpp.orig @@ -387,6 +387,10 @@ struct Component::ComponentHelpers template static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p) { + float total_scaling = source->getTotalPixelScaling(); + Component* top = nullptr; + if (source) + top = source->getTopLevelComponent(); while (source != nullptr) { if (source == target) @@ -395,6 +399,9 @@ struct Component::ComponentHelpers if (source->isParentOf (target)) return convertFromDistantParentSpace (source, *target, p); + if (source == top) + p /= total_scaling; + p = convertToParentSpace (*source, p); source = source->getParentComponent(); } @@ -1390,13 +1397,14 @@ bool Component::reallyContains (Point point, bool returnTrueIfWithinAChild) Component* Component::getComponentAt (Point position) { + Point scale = (position.toFloat() * getPixelScaling()).roundToInt(); if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position)) { for (int i = childComponentList.size(); --i >= 0;) { auto* child = childComponentList.getUnchecked(i); - child = child->getComponentAt (ComponentHelpers::convertFromParentSpace (*child, position)); + child = child->getComponentAt (ComponentHelpers::convertFromParentSpace (*child, scale)); if (child != nullptr) return child; diff --git a/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.h b/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.h index ccb2681f..ca84bc80 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.h +++ b/libs/juce-current/source/modules/juce_gui_basics/components/juce_Component.h @@ -439,7 +439,7 @@ public: @see setBounds, ComponentListener::componentMovedOrResized */ - void setTopLeftPosition (Point newTopLeftPosition); + virtual void setTopLeftPosition (Point newTopLeftPosition); /** Moves the component to a new position. @@ -2179,10 +2179,7 @@ public: operator ComponentType*() const noexcept { return getComponent(); } /** Returns the component that this pointer refers to, or null if the component no longer exists. */ - ComponentType* operator->() noexcept { return getComponent(); } - - /** Returns the component that this pointer refers to, or null if the component no longer exists. */ - const ComponentType* operator->() const noexcept { return getComponent(); } + ComponentType* operator->() const noexcept { return getComponent(); } /** If the component is valid, this deletes it and sets this pointer to null. */ void deleteAndZero() { delete getComponent(); } diff --git a/libs/juce-current/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp b/libs/juce-current/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp index 661c02a3..ac793777 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp @@ -158,7 +158,7 @@ bool FileChooser::showDialog (const int flags, FilePreviewComponent* const previ { FocusRestorer focusRestorer; - pimpl.reset (createPimpl (flags, previewComp)); + pimpl = createPimpl (flags, previewComp); pimpl->runModally(); // ensure that the finished function was invoked @@ -179,12 +179,12 @@ void FileChooser::launchAsync (int flags, std::functionlaunch(); } -FileChooser::Pimpl* FileChooser::createPimpl (int flags, FilePreviewComponent* previewComp) +std::shared_ptr FileChooser::createPimpl (int flags, FilePreviewComponent* previewComp) { results.clear(); @@ -214,10 +214,8 @@ FileChooser::Pimpl* FileChooser::createPimpl (int flags, FilePreviewComponent* p { return showPlatformDialog (*this, flags, previewComp); } - else - { - return new NonNative (*this, flags, previewComp); - } + + return std::make_unique (*this, flags, previewComp); } Array FileChooser::getResults() const noexcept diff --git a/libs/juce-current/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.h b/libs/juce-current/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.h index e1a50ee9..df6197b5 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.h +++ b/libs/juce-current/source/modules/juce_gui_basics/filebrowser/juce_FileChooser.h @@ -325,12 +325,11 @@ private: virtual void runModally() = 0; }; - std::unique_ptr pimpl; + std::shared_ptr pimpl; //============================================================================== - Pimpl* createPimpl (int, FilePreviewComponent*); - static Pimpl* showPlatformDialog (FileChooser&, int, - FilePreviewComponent*); + std::shared_ptr createPimpl (int, FilePreviewComponent*); + static std::shared_ptr showPlatformDialog (FileChooser&, int, FilePreviewComponent*); class NonNative; friend class NonNative; diff --git a/libs/juce-current/source/modules/juce_gui_basics/juce_gui_basics.cpp b/libs/juce-current/source/modules/juce_gui_basics/juce_gui_basics.cpp index 45879fbf..bda0fd97 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/juce_gui_basics.cpp @@ -41,6 +41,7 @@ #define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1 #define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 #define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1 +#define JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER 1 #include "juce_gui_basics.h" diff --git a/libs/juce-current/source/modules/juce_gui_basics/juce_gui_basics.h b/libs/juce-current/source/modules/juce_gui_basics/juce_gui_basics.h index 85fc9c4a..38630e2b 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/juce_gui_basics.h +++ b/libs/juce-current/source/modules/juce_gui_basics/juce_gui_basics.h @@ -341,6 +341,10 @@ namespace juce #endif #endif +#if JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER && JUCE_WINDOWS + #include "native/juce_win32_ScopedThreadDPIAwarenessSetter.h" +#endif + #include "layout/juce_FlexItem.h" #include "layout/juce_FlexBox.h" diff --git a/libs/juce-current/source/modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp b/libs/juce-current/source/modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp index 7c6b83fb..a84cf5a9 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp @@ -43,14 +43,9 @@ void CaretComponent::paint (Graphics& g) g.fillRect (getLocalBounds()); } -void CaretComponent::timerCallback() -{ - setVisible (shouldBeShown() && ! isVisible()); -} void CaretComponent::setCaretPosition (const Rectangle& characterArea) { - startTimer (380); setVisible (shouldBeShown()); setBounds (characterArea.withWidth (2)); } diff --git a/libs/juce-current/source/modules/juce_gui_basics/keyboard/juce_CaretComponent.h b/libs/juce-current/source/modules/juce_gui_basics/keyboard/juce_CaretComponent.h index a10b26c6..97894c07 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/keyboard/juce_CaretComponent.h +++ b/libs/juce-current/source/modules/juce_gui_basics/keyboard/juce_CaretComponent.h @@ -31,8 +31,7 @@ namespace juce @tags{GUI} */ -class JUCE_API CaretComponent : public Component, - private Timer +class JUCE_API CaretComponent : public Component { public: //============================================================================== @@ -73,7 +72,6 @@ private: Component* owner; bool shouldBeShown() const; - void timerCallback() override; JUCE_DECLARE_NON_COPYABLE (CaretComponent) }; diff --git a/libs/juce-current/source/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.cpp b/libs/juce-current/source/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.cpp index 9ea63b21..7e8f1499 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.cpp @@ -271,23 +271,6 @@ void ComponentBoundsConstrainer::checkBounds (Rectangle& bounds, bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio)); } } - - if ((isStretchingTop || isStretchingBottom) && ! (isStretchingLeft || isStretchingRight)) - { - bounds.setX (old.getX() + (old.getWidth() - bounds.getWidth()) / 2); - } - else if ((isStretchingLeft || isStretchingRight) && ! (isStretchingTop || isStretchingBottom)) - { - bounds.setY (old.getY() + (old.getHeight() - bounds.getHeight()) / 2); - } - else - { - if (isStretchingLeft) - bounds.setX (old.getRight() - bounds.getWidth()); - - if (isStretchingTop) - bounds.setY (old.getBottom() - bounds.getHeight()); - } } jassert (! bounds.isEmpty()); diff --git a/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.cpp b/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.cpp index 738fed8c..f9f79994 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.cpp @@ -41,8 +41,6 @@ LookAndFeel_V1::LookAndFeel_V1() setColour (PopupMenu::highlightedBackgroundColourId, Colour (0xbfa4c2ce)); setColour (PopupMenu::highlightedTextColourId, Colours::black); setColour (TextEditor::focusedOutlineColourId, findColour (TextButton::buttonColourId)); - - scrollbarShadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 2, Point())); } LookAndFeel_V1::~LookAndFeel_V1() @@ -291,7 +289,7 @@ void LookAndFeel_V1::drawScrollbar (Graphics& g, ScrollBar& bar, ImageEffectFilter* LookAndFeel_V1::getScrollbarEffect() { - return &scrollbarShadow; + return nullptr; } @@ -477,7 +475,7 @@ Button* LookAndFeel_V1::createSliderButton (Slider&, const bool isIncrement) ImageEffectFilter* LookAndFeel_V1::getSliderEffect (Slider&) { - return &scrollbarShadow; + return nullptr; } int LookAndFeel_V1::getSliderThumbRadius (Slider&) diff --git a/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h b/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h index b6c05aed..51508a57 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h +++ b/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h @@ -97,8 +97,6 @@ public: bool positionTitleBarButtonsOnLeft) override; private: - DropShadowEffect scrollbarShadow; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookAndFeel_V1) }; diff --git a/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp b/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp index 21042d0b..829fed8e 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp @@ -1292,8 +1292,6 @@ void LookAndFeel_V4::drawCallOutBoxBackground (CallOutBox& box, Graphics& g, { cachedImage = { Image::ARGB, box.getWidth(), box.getHeight(), true }; Graphics g2 (cachedImage); - - DropShadow (Colours::black.withAlpha (0.7f), 8, { 0, 2 }).drawForPath (g2, path); } g.setColour (Colours::black); diff --git a/libs/juce-current/source/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp b/libs/juce-current/source/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp index 7c084864..2017bccb 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/misc/juce_BubbleComponent.cpp @@ -30,9 +30,6 @@ BubbleComponent::BubbleComponent() : allowablePlacements (above | below | left | right) { setInterceptsMouseClicks (false, false); - - shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.35f), 5, Point())); - setComponentEffect (&shadow); } BubbleComponent::~BubbleComponent() {} diff --git a/libs/juce-current/source/modules/juce_gui_basics/misc/juce_BubbleComponent.h b/libs/juce-current/source/modules/juce_gui_basics/misc/juce_BubbleComponent.h index f086c8ff..cca6205a 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/misc/juce_BubbleComponent.h +++ b/libs/juce-current/source/modules/juce_gui_basics/misc/juce_BubbleComponent.h @@ -178,7 +178,6 @@ private: Rectangle content; Point arrowTip; int allowablePlacements; - DropShadowEffect shadow; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BubbleComponent) }; diff --git a/libs/juce-current/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp b/libs/juce-current/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp index 803740c6..1920941d 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp @@ -258,10 +258,10 @@ bool FileChooser::isPlatformDialogAvailable() #endif } -FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags, FilePreviewComponent*) +std::shared_ptr FileChooser::showPlatformDialog (FileChooser& owner, int flags, FilePreviewComponent*) { #if JUCE_MODAL_LOOPS_PERMITTED - return new Native (owner, flags); + return std::make_shared (owner, flags); #else return nullptr; #endif diff --git a/libs/juce-current/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/libs/juce-current/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index ea597e33..453abc84 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -85,8 +85,8 @@ public: updateScaleFactorFromNewBounds (bounds, false); - auto physicalBounds = (parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (bounds) - : bounds * currentScaleFactor); + auto physicalBounds = parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (bounds) + : bounds * currentScaleFactor; WeakReference deletionChecker (&component); @@ -103,13 +103,16 @@ public: Point getScreenPosition (bool physical) const { - auto parentPosition = XWindowSystem::getInstance()->getParentScreenPosition(); + auto physicalParentPosition = XWindowSystem::getInstance()->getPhysicalParentScreenPosition(); + auto parentPosition = parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalParentPosition) + : physicalParentPosition / currentScaleFactor; - auto screenBounds = (parentWindow == 0 ? bounds - : bounds.translated (parentPosition.x, parentPosition.y)); + auto screenBounds = parentWindow == 0 ? bounds + : bounds.translated (parentPosition.x, parentPosition.y); if (physical) - return Desktop::getInstance().getDisplays().logicalToPhysical (screenBounds.getTopLeft()); + return parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (screenBounds.getTopLeft()) + : screenBounds.getTopLeft() * currentScaleFactor; return screenBounds.getTopLeft(); } @@ -314,8 +317,8 @@ public: updateScaleFactorFromNewBounds (physicalBounds, true); - bounds = (parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalBounds) - : physicalBounds / currentScaleFactor); + bounds = parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalBounds) + : physicalBounds / currentScaleFactor; } } @@ -433,9 +436,6 @@ private: //============================================================================== void updateScaleFactorFromNewBounds (const Rectangle& newBounds, bool isPhysical) { - if (! JUCEApplicationBase::isStandaloneApp()) - return; - Point translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point()); const auto& desktop = Desktop::getInstance(); diff --git a/libs/juce-current/source/modules/juce_gui_basics/native/juce_mac_FileChooser.mm b/libs/juce-current/source/modules/juce_gui_basics/native/juce_mac_FileChooser.mm index af5d96af..61febe1b 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/native/juce_mac_FileChooser.mm +++ b/libs/juce-current/source/modules/juce_gui_basics/native/juce_mac_FileChooser.mm @@ -377,10 +377,10 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Native) }; -FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags, - FilePreviewComponent* preview) +std::shared_ptr FileChooser::showPlatformDialog (FileChooser& owner, int flags, + FilePreviewComponent* preview) { - return new FileChooser::Native (owner, flags, preview); + return std::make_shared (owner, flags, preview); } bool FileChooser::isPlatformDialogAvailable() diff --git a/libs/juce-current/source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp b/libs/juce-current/source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp old mode 100644 new mode 100755 index a5b42b8e..1e098d7d --- a/libs/juce-current/source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp @@ -26,32 +26,23 @@ namespace juce { -// Win32NativeFileChooser needs to be a reference counted object as there -// is no way for the parent to know when the dialog HWND has actually been -// created without pumping the message thread (which is forbidden when modal -// loops are disabled). However, the HWND pointer is the only way to cancel -// the dialog box. This means that the actual native FileChooser HWND may -// not have been created yet when the user deletes JUCE's FileChooser class. If this -// occurs the Win32NativeFileChooser will still have a reference count of 1 and will -// simply delete itself immediately once the HWND will have been created a while later. -class Win32NativeFileChooser : public ReferenceCountedObject, +class Win32NativeFileChooser : public std::enable_shared_from_this, private Thread { public: - using Ptr = ReferenceCountedObjectPtr; - enum { charsAvailableForResult = 32768 }; Win32NativeFileChooser (Component* parent, int flags, FilePreviewComponent* previewComp, const File& startingFile, const String& titleToUse, const String& filtersToUse) : Thread ("Native Win32 FileChooser"), - owner (parent), title (titleToUse), filtersString (filtersToUse.replaceCharacter (',', ';')), + owner (parent), + title (titleToUse), + filtersString (filtersToUse.replaceCharacter (',', ';')), selectsDirectories ((flags & FileBrowserComponent::canSelectDirectories) != 0), isSave ((flags & FileBrowserComponent::saveMode) != 0), warnAboutOverwrite ((flags & FileBrowserComponent::warnAboutOverwriting) != 0), - selectMultiple ((flags & FileBrowserComponent::canSelectMultipleItems) != 0), - nativeDialogRef (nullptr), shouldCancel (0) + selectMultiple ((flags & FileBrowserComponent::canSelectMultipleItems) != 0) { auto parentDirectory = startingFile.getParentDirectory(); @@ -91,14 +82,13 @@ public: // the thread should not be running nativeDialogRef.set (nullptr); + weakThis = shared_from_this(); + if (async) { jassert (! isThreadRunning()); - threadHasReference.reset(); startThread(); - - threadHasReference.wait (-1); } else { @@ -112,7 +102,7 @@ public: ScopedLock lock (deletingDialog); customComponent = nullptr; - shouldCancel.set (1); + shouldCancel = true; if (auto hwnd = nativeDialogRef.get()) EndDialog (hwnd, 0); @@ -151,12 +141,12 @@ private: }; //============================================================================== - Component::SafePointer owner; + const Component::SafePointer owner; + std::weak_ptr weakThis; String title, filtersString; std::unique_ptr customComponent; String initialPath, returnedString; - WaitableEvent threadHasReference; CriticalSection deletingDialog; bool selectsDirectories, isSave, warnAboutOverwrite, selectMultiple; @@ -164,8 +154,8 @@ private: HeapBlock files; HeapBlock filters; - Atomic nativeDialogRef; - Atomic shouldCancel; + Atomic nativeDialogRef { nullptr }; + bool shouldCancel = false; struct FreeLPWSTR { @@ -173,7 +163,7 @@ private: }; #if JUCE_MSVC - bool showDialog (IFileDialog& dialog, bool async) const + bool showDialog (IFileDialog& dialog, bool async) { FILEOPENDIALOGOPTIONS flags = {}; @@ -236,7 +226,49 @@ private: if (! selectsDirectories && FAILED (dialog.SetFileTypes (numElementsInArray (spec), spec))) return false; - return dialog.Show (static_cast (async ? nullptr : owner->getWindowHandle())) == S_OK; + struct Events : public ComBaseClassHelper + { + explicit Events (Win32NativeFileChooser& o) : owner (o) {} + + JUCE_COMRESULT OnTypeChange (IFileDialog* d) override + { + HWND hwnd = nullptr; + IUnknown_GetWindow (d, &hwnd); + + ScopedLock lock (owner.deletingDialog); + + if (hwnd != nullptr) + owner.nativeDialogRef = hwnd; + + return owner.shouldCancel ? S_FALSE : S_OK; + } + + JUCE_COMRESULT OnFolderChanging (IFileDialog*, IShellItem*) override { return S_OK; } + JUCE_COMRESULT OnFileOk (IFileDialog*) override { return S_OK; } + JUCE_COMRESULT OnFolderChange (IFileDialog*) override { return S_OK; } + JUCE_COMRESULT OnSelectionChange (IFileDialog*) override { return S_OK; } + JUCE_COMRESULT OnShareViolation (IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) override { return S_OK; } + JUCE_COMRESULT OnOverwrite (IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) override { return S_OK; } + + Win32NativeFileChooser& owner; + }; + + DWORD cookie = 0; + dialog.Advise (new Events { *this }, &cookie); + + { + ScopedLock lock (deletingDialog); + + if (shouldCancel) + return false; + } + + const auto result = dialog.Show (async ? nullptr : static_cast (owner->getWindowHandle())) == S_OK; + + ScopedLock lock (deletingDialog); + nativeDialogRef = nullptr; + + return result; } //============================================================================== @@ -451,33 +483,21 @@ private: void run() override { - // We use a functor rather than a lambda here because - // we want to move ownership of the Ptr into the function - // object, and C++11 doesn't support general lambda capture - struct AsyncCallback - { - AsyncCallback (Ptr p, Array r) - : ptr (std::move (p)), - results (std::move (r)) {} - - void operator()() - { - ptr->results = std::move (results); - - if (ptr->owner != nullptr) - ptr->owner->exitModalState (ptr->results.size() > 0 ? 1 : 0); - } + // IUnknown_GetWindow will only succeed when instantiated in a single-thread apartment + CoInitializeEx (nullptr, COINIT_APARTMENTTHREADED); - Ptr ptr; - Array results; - }; + auto resultsCopy = openDialog (true); + auto safeOwner = owner; + auto weakThisCopy = weakThis; - // as long as the thread is running, don't delete this class - Ptr safeThis (this); - threadHasReference.signal(); + MessageManager::callAsync ([resultsCopy, safeOwner, weakThisCopy] + { + if (auto locked = weakThisCopy.lock()) + locked->results = resultsCopy; - auto r = openDialog (true); - MessageManager::callAsync (AsyncCallback (std::move (safeThis), std::move (r))); + if (safeOwner != nullptr) + safeOwner->exitModalState (resultsCopy.size() > 0 ? 1 : 0); + }); } static HashMap& getNativeDialogList() @@ -486,9 +506,9 @@ private: return dialogs; } - static Win32NativeFileChooser* getNativePointerForDialog (HWND hWnd) + static Win32NativeFileChooser* getNativePointerForDialog (HWND hwnd) { - return getNativeDialogList()[hWnd]; + return getNativeDialogList()[hwnd]; } //============================================================================== @@ -556,7 +576,7 @@ private: ScopedLock lock (deletingDialog); getNativeDialogList().set (hdlg, this); - if (shouldCancel.get() != 0) + if (shouldCancel) { EndDialog (hdlg, 0); } @@ -621,7 +641,7 @@ private: { ScopedLock lock (deletingDialog); - if (customComponent != nullptr && shouldCancel.get() == 0) + if (customComponent != nullptr && ! shouldCancel) { if (FilePreviewComponent* comp = dynamic_cast (customComponent->getChildComponent (0))) { @@ -719,14 +739,15 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32NativeFileChooser) }; -class FileChooser::Native : public Component, +class FileChooser::Native : public std::enable_shared_from_this, + public Component, public FileChooser::Pimpl { public: Native (FileChooser& fileChooser, int flags, FilePreviewComponent* previewComp) : owner (fileChooser), - nativeFileChooser (new Win32NativeFileChooser (this, flags, previewComp, fileChooser.startingFile, - fileChooser.title, fileChooser.filters)) + nativeFileChooser (std::make_shared (this, flags, previewComp, fileChooser.startingFile, + fileChooser.title, fileChooser.filters)) { auto mainMon = Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea; @@ -743,19 +764,17 @@ public: { exitModalState (0); nativeFileChooser->cancel(); - nativeFileChooser = nullptr; } void launch() override { - SafePointer safeThis (this); + std::weak_ptr safeThis = shared_from_this(); - enterModalState (true, ModalCallbackFunction::create ( - [safeThis] (int) - { - if (safeThis != nullptr) - safeThis->owner.finished (safeThis->nativeFileChooser->results); - })); + enterModalState (true, ModalCallbackFunction::create ([safeThis] (int) + { + if (auto locked = safeThis.lock()) + locked->owner.finished (locked->nativeFileChooser->results); + })); nativeFileChooser->open (true); } @@ -787,7 +806,7 @@ public: private: FileChooser& owner; - Win32NativeFileChooser::Ptr nativeFileChooser; + std::shared_ptr nativeFileChooser; }; //============================================================================== @@ -800,10 +819,10 @@ bool FileChooser::isPlatformDialogAvailable() #endif } -FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags, - FilePreviewComponent* preview) +std::shared_ptr FileChooser::showPlatformDialog (FileChooser& owner, int flags, + FilePreviewComponent* preview) { - return new FileChooser::Native (owner, flags, preview); + return std::make_shared (owner, flags, preview); } } // namespace juce diff --git a/libs/juce-current/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/libs/juce-current/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp old mode 100755 new mode 100644 index 43eeb665..e92e9af1 --- a/libs/juce-current/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -27,7 +27,7 @@ #include #endif -#if JUCE_WIN_PER_MONITOR_DPI_AWARE && JUCE_MODULE_AVAILABLE_juce_gui_extra +#if JUCE_MODULE_AVAILABLE_juce_gui_extra #include #endif @@ -63,6 +63,31 @@ static bool shouldDeactivateTitleBar = true; void* getUser32Function (const char*); +#if JUCE_DEBUG + int numActiveScopedDpiAwarenessDisablers = 0; + bool isInScopedDPIAwarenessDisabler() { return numActiveScopedDpiAwarenessDisablers > 0; } + extern HWND juce_messageWindowHandle; +#endif + +struct ScopedDeviceContext +{ + explicit ScopedDeviceContext (HWND h) + : hwnd (h), dc (GetDC (hwnd)) + { + } + + ~ScopedDeviceContext() + { + ReleaseDC (hwnd, dc); + } + + HWND hwnd; + HDC dc; + + JUCE_DECLARE_NON_COPYABLE (ScopedDeviceContext) + JUCE_DECLARE_NON_MOVEABLE (ScopedDeviceContext) +}; + //============================================================================== #ifndef WM_TOUCH enum @@ -408,7 +433,9 @@ static void setDPIAwareness() static bool isPerMonitorDPIAwareProcess() { - #if JUCE_WIN_PER_MONITOR_DPI_AWARE + #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE + return false; + #else static bool dpiAware = []() -> bool { setDPIAwareness(); @@ -423,39 +450,43 @@ static bool isPerMonitorDPIAwareProcess() }(); return dpiAware; - #else - return false; #endif } -static bool isPerMonitorDPIAwareWindow (HWND h) +static bool isPerMonitorDPIAwareWindow (HWND nativeWindow) { - #if JUCE_WIN_PER_MONITOR_DPI_AWARE - jassert (h != nullptr); - + #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE + ignoreUnused (nativeWindow); + return false; + #else setDPIAwareness(); - if (getWindowDPIAwarenessContext != nullptr && getAwarenessFromDPIAwarenessContext != nullptr) - return getAwarenessFromDPIAwarenessContext (getWindowDPIAwarenessContext (h)) == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware; + if (getWindowDPIAwarenessContext != nullptr + && getAwarenessFromDPIAwarenessContext != nullptr) + { + return (getAwarenessFromDPIAwarenessContext (getWindowDPIAwarenessContext (nativeWindow)) + == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware); + } return isPerMonitorDPIAwareProcess(); - #else - ignoreUnused (h); - return false; #endif } static bool isPerMonitorDPIAwareThread() { - #if JUCE_WIN_PER_MONITOR_DPI_AWARE + #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE + return false; + #else setDPIAwareness(); - if (getThreadDPIAwarenessContext != nullptr && getAwarenessFromDPIAwarenessContext != nullptr) - return getAwarenessFromDPIAwarenessContext (getThreadDPIAwarenessContext()) == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware; + if (getThreadDPIAwarenessContext != nullptr + && getAwarenessFromDPIAwarenessContext != nullptr) + { + return (getAwarenessFromDPIAwarenessContext (getThreadDPIAwarenessContext()) + == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware); + } return isPerMonitorDPIAwareProcess(); - #else - return false; #endif } @@ -463,27 +494,114 @@ static double getGlobalDPI() { setDPIAwareness(); - HDC dc = GetDC (nullptr); - auto dpi = (GetDeviceCaps (dc, LOGPIXELSX) + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0; - ReleaseDC (nullptr, dc); - return dpi; + ScopedDeviceContext deviceContext { nullptr }; + return (GetDeviceCaps (deviceContext.dc, LOGPIXELSX) + GetDeviceCaps (deviceContext.dc, LOGPIXELSY)) / 2.0; } //============================================================================== -#if JUCE_WIN_PER_MONITOR_DPI_AWARE && JUCE_MODULE_AVAILABLE_juce_gui_extra +class ScopedThreadDPIAwarenessSetter::NativeImpl +{ +public: + explicit NativeImpl (HWND nativeWindow) + { + ignoreUnused (nativeWindow); + + #if JUCE_WIN_PER_MONITOR_DPI_AWARE + if (auto* functionSingleton = FunctionSingleton::getInstance()) + { + if (! functionSingleton->isLoaded()) + return; + + auto dpiAwareWindow = (functionSingleton->getAwarenessFromContext (functionSingleton->getWindowAwareness (nativeWindow)) + == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware); + + auto dpiAwareThread = (functionSingleton->getAwarenessFromContext (functionSingleton->getThreadAwareness()) + == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware); + + if (dpiAwareWindow && ! dpiAwareThread) + oldContext = functionSingleton->setThreadAwareness (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); + else if (! dpiAwareWindow && dpiAwareThread) + oldContext = functionSingleton->setThreadAwareness (DPI_AWARENESS_CONTEXT_UNAWARE); + } + #endif + } + + ~NativeImpl() + { + if (oldContext != nullptr) + if (auto* functionSingleton = FunctionSingleton::getInstance()) + functionSingleton->setThreadAwareness (oldContext); + } + +private: + struct FunctionSingleton : public DeletedAtShutdown + { + FunctionSingleton() = default; + ~FunctionSingleton() override { clearSingletonInstance(); } + + SetThreadDPIAwarenessContextFunc setThreadAwareness = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext"); + GetWindowDPIAwarenessContextFunc getWindowAwareness = (GetWindowDPIAwarenessContextFunc) getUser32Function ("GetWindowDpiAwarenessContext"); + GetThreadDPIAwarenessContextFunc getThreadAwareness = (GetThreadDPIAwarenessContextFunc) getUser32Function ("GetThreadDpiAwarenessContext"); + GetAwarenessFromDpiAwarenessContextFunc getAwarenessFromContext = (GetAwarenessFromDpiAwarenessContextFunc) getUser32Function ("GetAwarenessFromDpiAwarenessContext"); + + bool isLoaded() const noexcept + { + return setThreadAwareness != nullptr + && getWindowAwareness != nullptr + && getThreadAwareness != nullptr + && getAwarenessFromContext != nullptr; + } + + JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (FunctionSingleton) + + JUCE_DECLARE_NON_COPYABLE (FunctionSingleton) + JUCE_DECLARE_NON_MOVEABLE (FunctionSingleton) + }; + + DPI_AWARENESS_CONTEXT oldContext = nullptr; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeImpl) + JUCE_DECLARE_NON_MOVEABLE (NativeImpl) +}; + + +JUCE_IMPLEMENT_SINGLETON (ScopedThreadDPIAwarenessSetter::NativeImpl::FunctionSingleton) + +ScopedThreadDPIAwarenessSetter::ScopedThreadDPIAwarenessSetter (void* nativeWindow) +{ + pimpl = std::make_unique ((HWND) nativeWindow); +} + +ScopedThreadDPIAwarenessSetter::~ScopedThreadDPIAwarenessSetter() +{ +} + +#if JUCE_MODULE_AVAILABLE_juce_gui_extra ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { if (! isPerMonitorDPIAwareThread()) return; if (setThreadDPIAwarenessContext != nullptr) + { previousContext = setThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE); + + #if JUCE_DEBUG + ++numActiveScopedDpiAwarenessDisablers; + #endif + } } ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() { if (previousContext != nullptr) + { setThreadDPIAwarenessContext ((DPI_AWARENESS_CONTEXT) previousContext); + + #if JUCE_DEBUG + --numActiveScopedDpiAwarenessDisablers; + #endif + } } #endif @@ -527,6 +645,14 @@ static Point convertPhysicalScreenPointToLogical (Point p, HWND h) noe return p; } +static Point convertLogicalScreenPointToPhysical (Point p, HWND h) noexcept +{ + if (isPerMonitorDPIAwareWindow (h)) + return Desktop::getInstance().getDisplays().logicalToPhysical (p, getCurrentDisplayFromScaleFactor (h)); + + return p; +} + JUCE_API double getScaleFactorForWindow (HWND h) { // NB. Using a local function here because we need to call this method from the plug-in wrappers @@ -549,50 +675,11 @@ JUCE_API double getScaleFactorForWindow (HWND h) return 1.0; } -JUCE_API void setThreadDPIAwarenessForWindow (HWND nativeWindow) -{ - #if JUCE_WIN_PER_MONITOR_DPI_AWARE - // NB. Using local functions here because we need to call this method from the plug-in wrappers - // which don't load the DPI-awareness functions on startup - static SetThreadDPIAwarenessContextFunc localSetThreadDPIAwarenessContext = nullptr; - static GetWindowDPIAwarenessContextFunc localGetWindowDPIAwarenessContext = nullptr; - static GetThreadDPIAwarenessContextFunc localGetThreadDPIAwarenessContext = nullptr; - static GetAwarenessFromDpiAwarenessContextFunc localGetAwarenessFromDPIAwarenessContext = nullptr; - - static bool hasChecked = false; - static bool loadedOK = false; - - if (! hasChecked) - { - hasChecked = true; - - localSetThreadDPIAwarenessContext = (SetThreadDPIAwarenessContextFunc) getUser32Function ("SetThreadDpiAwarenessContext"); - localGetWindowDPIAwarenessContext = (GetWindowDPIAwarenessContextFunc) getUser32Function ("GetWindowDpiAwarenessContext"); - localGetThreadDPIAwarenessContext = (GetThreadDPIAwarenessContextFunc) getUser32Function ("GetThreadDpiAwarenessContext"); - localGetAwarenessFromDPIAwarenessContext = (GetAwarenessFromDpiAwarenessContextFunc) getUser32Function ("GetAwarenessFromDpiAwarenessContext"); - - loadedOK = (localSetThreadDPIAwarenessContext != nullptr && localGetWindowDPIAwarenessContext != nullptr - && localGetThreadDPIAwarenessContext != nullptr && localGetAwarenessFromDPIAwarenessContext != nullptr); - } - - if (loadedOK) - { - auto dpiAwareWindow = localGetAwarenessFromDPIAwarenessContext (localGetWindowDPIAwarenessContext (nativeWindow)) == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware; - auto dpiAwareThread = localGetAwarenessFromDPIAwarenessContext (localGetThreadDPIAwarenessContext()) == DPI_Awareness::DPI_Awareness_Per_Monitor_Aware; - - if (dpiAwareWindow && ! dpiAwareThread) - localSetThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); - else if (! dpiAwareWindow && dpiAwareThread) - localSetThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE); - } - #else - ignoreUnused (nativeWindow); - #endif -} - //============================================================================== static void setWindowPos (HWND hwnd, Rectangle bounds, UINT flags, bool adjustTopLeft = false) { + ScopedThreadDPIAwarenessSetter setter { hwnd }; + if (isPerMonitorDPIAwareWindow (hwnd)) { if (adjustTopLeft) @@ -607,9 +694,7 @@ static void setWindowPos (HWND hwnd, Rectangle bounds, UINT flags, bool adj static RECT getWindowScreenRect (HWND hwnd) { - #if JUCE_WIN_PER_MONITOR_DPI_AWARE - setThreadDPIAwarenessForWindow (hwnd); - #endif + ScopedThreadDPIAwarenessSetter setter { hwnd }; RECT rect; GetWindowRect (hwnd, &rect); @@ -621,7 +706,10 @@ static RECT getWindowClientRect (HWND hwnd) auto rect = getWindowScreenRect (hwnd); if (auto parentH = GetParent (hwnd)) + { + ScopedThreadDPIAwarenessSetter setter { hwnd }; MapWindowPoints (HWND_DESKTOP, parentH, (LPPOINT) &rect, 2); + } return rect; } @@ -634,14 +722,8 @@ static void setWindowZOrder (HWND hwnd, HWND insertAfter) //============================================================================== double Desktop::getDefaultMasterScale() { - if (! JUCEApplicationBase::isStandaloneApp() - #if JUCE_WIN_PER_MONITOR_DPI_AWARE - || isPerMonitorDPIAwareProcess() - #endif - ) - { + if (! JUCEApplicationBase::isStandaloneApp() || isPerMonitorDPIAwareProcess()) return 1.0; - } return getGlobalDPI() / USER_DEFAULT_SCREEN_DPI; } @@ -782,9 +864,10 @@ public: bitmapInfo.bV4V4Compression = BI_RGB; } - HDC dc = GetDC (nullptr); - hdc = CreateCompatibleDC (dc); - ReleaseDC (nullptr, dc); + { + ScopedDeviceContext deviceContext { nullptr }; + hdc = CreateCompatibleDC (deviceContext.dc); + } SetMapMode (hdc, MM_TEXT); @@ -877,10 +960,8 @@ public: private: static bool isGraphicsCard32Bit() { - auto dc = GetDC (nullptr); - auto bitsPerPixel = GetDeviceCaps (dc, BITSPIXEL); - ReleaseDC (nullptr, dc); - return bitsPerPixel > 24; + ScopedDeviceContext deviceContext { nullptr }; + return GetDeviceCaps (deviceContext.dc, BITSPIXEL) > 24; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsBitmapImage) @@ -893,13 +974,13 @@ Image createSnapshotOfNativeWindow (void* nativeWindowHandle) auto hwnd = (HWND) nativeWindowHandle; auto r = convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (hwnd)), hwnd); - const int w = r.getWidth(); - const int h = r.getHeight(); + const auto w = r.getWidth(); + const auto h = r.getHeight(); auto nativeBitmap = new WindowsBitmapImage (Image::RGB, w, h, true); Image bitmap (nativeBitmap); - HDC dc = GetDC (hwnd); + ScopedDeviceContext deviceContext { hwnd }; if (isPerMonitorDPIAwareProcess()) { @@ -908,18 +989,16 @@ Image createSnapshotOfNativeWindow (void* nativeWindowHandle) SetBrushOrgEx (nativeBitmap->hdc, 0, 0, NULL); StretchBlt (nativeBitmap->hdc, 0, 0, w, h, - dc, 0, 0, roundToInt (w * scale), roundToInt (h * scale), + deviceContext.dc, 0, 0, roundToInt (w * scale), roundToInt (h * scale), SRCCOPY); SetStretchBltMode (nativeBitmap->hdc, prevStretchMode); } else { - BitBlt (nativeBitmap->hdc, 0, 0, w, h, dc, 0, 0, SRCCOPY); + BitBlt (nativeBitmap->hdc, 0, 0, w, h, deviceContext.dc, 0, 0, SRCCOPY); } - ReleaseDC (hwnd, dc); - return SoftwareImageType().convert (bitmap); } @@ -960,79 +1039,75 @@ namespace IconConverters && bm.bmWidth > 0 && bm.bmHeight > 0)) return {}; - if (auto* tempDC = ::GetDC (nullptr)) + ScopedDeviceContext deviceContext { nullptr }; + + if (auto* dc = ::CreateCompatibleDC (deviceContext.dc)) { - if (auto* dc = ::CreateCompatibleDC (tempDC)) + BITMAPV5HEADER header = {}; + header.bV5Size = sizeof (BITMAPV5HEADER); + header.bV5Width = bm.bmWidth; + header.bV5Height = -bm.bmHeight; + header.bV5Planes = 1; + header.bV5Compression = BI_RGB; + header.bV5BitCount = 32; + header.bV5RedMask = 0x00FF0000; + header.bV5GreenMask = 0x0000FF00; + header.bV5BlueMask = 0x000000FF; + header.bV5AlphaMask = 0xFF000000; + header.bV5CSType = LCS_WINDOWS_COLOR_SPACE; + header.bV5Intent = LCS_GM_IMAGES; + + uint32* bitmapImageData = nullptr; + + if (auto* dib = ::CreateDIBSection (deviceContext.dc, (BITMAPINFO*) &header, DIB_RGB_COLORS, + (void**) &bitmapImageData, nullptr, 0)) { - BITMAPV5HEADER header = {}; - header.bV5Size = sizeof (BITMAPV5HEADER); - header.bV5Width = bm.bmWidth; - header.bV5Height = -bm.bmHeight; - header.bV5Planes = 1; - header.bV5Compression = BI_RGB; - header.bV5BitCount = 32; - header.bV5RedMask = 0x00FF0000; - header.bV5GreenMask = 0x0000FF00; - header.bV5BlueMask = 0x000000FF; - header.bV5AlphaMask = 0xFF000000; - header.bV5CSType = LCS_WINDOWS_COLOR_SPACE; - header.bV5Intent = LCS_GM_IMAGES; - - uint32* bitmapImageData = nullptr; - - if (auto* dib = ::CreateDIBSection (tempDC, (BITMAPINFO*) &header, DIB_RGB_COLORS, - (void**) &bitmapImageData, nullptr, 0)) - { - auto oldObject = ::SelectObject (dc, dib); + auto oldObject = ::SelectObject (dc, dib); - auto numPixels = bm.bmWidth * bm.bmHeight; - auto numColourComponents = (size_t) numPixels * 4; + auto numPixels = bm.bmWidth * bm.bmHeight; + auto numColourComponents = (size_t) numPixels * 4; - // Windows icon data comes as two layers, an XOR mask which contains the bulk - // of the image data and an AND mask which provides the transparency. Annoyingly - // the XOR mask can also contain an alpha channel, in which case the transparency - // mask should not be applied, but there's no way to find out a priori if the XOR - // mask contains an alpha channel. + // Windows icon data comes as two layers, an XOR mask which contains the bulk + // of the image data and an AND mask which provides the transparency. Annoyingly + // the XOR mask can also contain an alpha channel, in which case the transparency + // mask should not be applied, but there's no way to find out a priori if the XOR + // mask contains an alpha channel. - HeapBlock opacityMask (numPixels); - memset (bitmapImageData, 0, numColourComponents); - ::DrawIconEx (dc, 0, 0, icon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_MASK); + HeapBlock opacityMask (numPixels); + memset (bitmapImageData, 0, numColourComponents); + ::DrawIconEx (dc, 0, 0, icon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_MASK); - for (int i = 0; i < numPixels; ++i) - opacityMask[i] = (bitmapImageData[i] == 0); - - Image result = Image (Image::ARGB, bm.bmWidth, bm.bmHeight, true); - Image::BitmapData imageData (result, Image::BitmapData::readWrite); + for (int i = 0; i < numPixels; ++i) + opacityMask[i] = (bitmapImageData[i] == 0); - memset (bitmapImageData, 0, numColourComponents); - ::DrawIconEx (dc, 0, 0, icon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_NORMAL); - memcpy (imageData.data, bitmapImageData, numColourComponents); + Image result = Image (Image::ARGB, bm.bmWidth, bm.bmHeight, true); + Image::BitmapData imageData (result, Image::BitmapData::readWrite); - auto imageHasAlphaChannel = [&imageData, numPixels]() - { - for (int i = 0; i < numPixels; ++i) - if (imageData.data[i * 4] != 0) - return true; - - return false; - }; + memset (bitmapImageData, 0, numColourComponents); + ::DrawIconEx (dc, 0, 0, icon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_NORMAL); + memcpy (imageData.data, bitmapImageData, numColourComponents); - if (! imageHasAlphaChannel()) - for (int i = 0; i < numPixels; ++i) - imageData.data[i * 4] = opacityMask[i] ? 0xff : 0x00; + auto imageHasAlphaChannel = [&imageData, numPixels]() + { + for (int i = 0; i < numPixels; ++i) + if (imageData.data[i * 4] != 0) + return true; - ::SelectObject (dc, oldObject); - ::DeleteObject(dib); - ::DeleteDC (dc); - ::ReleaseDC (nullptr, tempDC); + return false; + }; - return result; - } + if (! imageHasAlphaChannel()) + for (int i = 0; i < numPixels; ++i) + imageData.data[i * 4] = opacityMask[i] ? 0xff : 0x00; + ::SelectObject (dc, oldObject); + ::DeleteObject (dib); ::DeleteDC (dc); + + return result; } - ::ReleaseDC (nullptr, tempDC); + ::DeleteDC (dc); } return {}; @@ -1433,10 +1508,8 @@ public: auto localBounds = rectangleFromRECT (getWindowClientRect (hwnd)); - #if JUCE_WIN_PER_MONITOR_DPI_AWARE if (isPerMonitorDPIAwareWindow (hwnd)) return (localBounds.toDouble() / getPlatformScaleFactor()).toNearestInt(); - #endif return localBounds; }(); @@ -1554,16 +1627,10 @@ public: if (! r.withZeroOrigin().contains (localPos)) return false; - auto globalPos = localPos + getScreenPosition(); - - #if JUCE_WIN_PER_MONITOR_DPI_AWARE - if (isPerMonitorDPIAwareThread() || isPerMonitorDPIAwareWindow (hwnd)) - globalPos = Desktop::getInstance().getDisplays().logicalToPhysical (globalPos); - #endif - - auto w = WindowFromPoint (POINTFromPoint (globalPos)); + auto w = WindowFromPoint (POINTFromPoint (convertLogicalScreenPointToPhysical (localPos + getScreenPosition(), + hwnd))); - return w == hwnd || (trueIfInAChildWindow && (IsChild (hwnd, w) != 0)); + return w == hwnd || (trueIfInAChildWindow && (IsChild (hwnd, w) != 0)); } BorderSize getFrameSize() const override @@ -1663,18 +1730,7 @@ public: void repaint (const Rectangle& area) override { - auto scale = getPlatformScaleFactor(); - - #if JUCE_WIN_PER_MONITOR_DPI_AWARE - // if the calling thread is DPI-aware but we are invalidating a non-DPI aware window RECT, we actually have to - // divide the bounds by the scale factor as it will get multiplied for the virtualised paint callback... - if (isPerMonitorDPIAwareThread() && ! isPerMonitorDPIAwareWindow (hwnd)) - scale = 1.0 / Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale; - #endif - - auto scaled = area.toDouble() * scale; - auto r = RECTFromRectangle (scaled.getSmallestIntegerContainer()); - + auto r = RECTFromRectangle ((area.toDouble() * getPlatformScaleFactor()).getSmallestIntegerContainer()); InvalidateRect (hwnd, &r, FALSE); } @@ -1760,7 +1816,7 @@ public: if (peerIsDeleted) return S_FALSE; - peer.handleDragExit (dragInfo); + peer.handleDragDrop (dragInfo); return S_OK; } @@ -1795,19 +1851,8 @@ public: private: Point getMousePos (POINTL mousePos) const { - auto screenPos = pointFromPOINT ({ mousePos.x, mousePos.y }).toFloat(); - - #if JUCE_WIN_PER_MONITOR_DPI_AWARE - auto h = (HWND) peer.getNativeHandle(); - - if (isPerMonitorDPIAwareWindow (h)) - screenPos = convertPhysicalScreenPointToLogical (screenPos.roundToInt(), h).toFloat(); - #else - if (JUCEApplication::isStandaloneApp()) - screenPos /= static_cast (getGlobalDPI() / USER_DEFAULT_SCREEN_DPI); - #endif - - return peer.getComponent().getLocalPoint (nullptr, screenPos); + return peer.getComponent().getLocalPoint (nullptr, convertPhysicalScreenPointToLogical (pointFromPOINT ({ mousePos.x, mousePos.y }), + (HWND) peer.getNativeHandle()).toFloat()); } struct DroppedData @@ -1902,7 +1947,9 @@ public: double getPlatformScaleFactor() const noexcept override { - #if JUCE_WIN_PER_MONITOR_DPI_AWARE + #if ! JUCE_WIN_PER_MONITOR_DPI_AWARE + return 1.0; + #else if (! isPerMonitorDPIAwareWindow (hwnd)) return 1.0; @@ -1916,8 +1963,6 @@ public: } return scaleFactor; - #else - return 1.0; #endif } @@ -2153,6 +2198,14 @@ private: L"", type, 0, 0, 0, 0, parentToAddTo, nullptr, (HINSTANCE) Process::getCurrentModuleInstanceHandle(), nullptr); + #if JUCE_DEBUG + // The DPI-awareness context of this window and JUCE's hidden message window are different. + // You normally want these to match otherwise timer events and async messages will happen + // in a different context to normal HWND messages which can cause issues with UI scaling. + jassert (isPerMonitorDPIAwareWindow (hwnd) == isPerMonitorDPIAwareWindow (juce_messageWindowHandle) + || isInScopedDPIAwarenessDisabler()); + #endif + if (hwnd != nullptr) { SetWindowLongPtr (hwnd, 0, 0); @@ -2179,19 +2232,8 @@ private: setDPIAwareness(); - #if JUCE_WIN_PER_MONITOR_DPI_AWARE if (isPerMonitorDPIAwareThread()) - { - auto bounds = component.getBounds(); - - if (bounds.isEmpty()) - scaleFactor = Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale; - else - scaleFactor = Desktop::getInstance().getDisplays().getDisplayForRect (bounds)->scale; - - scaleFactor /= Desktop::getInstance().getGlobalScaleFactor(); - } - #endif + scaleFactor = getScaleFactorForWindow (hwnd); setMessageFilter(); updateBorderSize(); @@ -3508,20 +3550,18 @@ private: Point getPointFromLocalLParam (LPARAM lParam) noexcept { - #if JUCE_WIN_PER_MONITOR_DPI_AWARE + auto p = pointFromPOINT (getPOINTFromLParam (lParam)); + if (isPerMonitorDPIAwareWindow (hwnd)) { // LPARAM is relative to this window's top-left but may be on a different monitor so we need to calculate the // physical screen position and then convert this to local logical coordinates - auto localPos = getPOINTFromLParam (lParam); auto r = getWindowScreenRect (hwnd); - - return globalToLocal (Desktop::getInstance().getDisplays().physicalToLogical (pointFromPOINT ({ r.left + localPos.x + roundToInt (windowBorder.getLeft() * scaleFactor), - r.top + localPos.y + roundToInt (windowBorder.getTop() * scaleFactor) })).toFloat()); + return globalToLocal (Desktop::getInstance().getDisplays().physicalToLogical (pointFromPOINT ({ r.left + p.x + roundToInt (windowBorder.getLeft() * scaleFactor), + r.top + p.y + roundToInt (windowBorder.getTop() * scaleFactor) })).toFloat()); } - #endif - return { static_cast (GET_X_LPARAM (lParam)), static_cast (GET_Y_LPARAM (lParam)) }; + return p.toFloat(); } Point getCurrentMousePos() noexcept @@ -4431,10 +4471,8 @@ Point MouseInputSource::getCurrentRawMousePosition() auto p = pointFromPOINT (mousePos); - #if JUCE_WIN_PER_MONITOR_DPI_AWARE if (isPerMonitorDPIAwareThread()) p = Desktop::getInstance().getDisplays().physicalToLogical (p); - #endif return p.toFloat(); } diff --git a/libs/juce-current/source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp b/libs/juce-current/source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp index e2194f65..44ae89ac 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp @@ -1849,14 +1849,14 @@ Rectangle XWindowSystem::getWindowBounds (::Window windowH, ::Window parent } else { - parentScreenPosition = Desktop::getInstance().getDisplays().physicalToLogical (Point (rootX, rootY)); + parentScreenPosition = Point (rootX, rootY); } } return { wx, wy, (int) ww, (int) wh }; } -Point XWindowSystem::getParentScreenPosition() const +Point XWindowSystem::getPhysicalParentScreenPosition() const { return parentScreenPosition; } @@ -2474,7 +2474,7 @@ Array XWindowSystem::findDisplays (float masterScale) const + ((static_cast (crtc->height) * 25.4 * 0.5) / static_cast (output->mm_height)); auto scale = DisplayHelpers::getDisplayScale (output->name, d.dpi); - scale = (scale <= 0.1 ? 1.0 : scale); + scale = (scale <= 0.1 || ! JUCEApplicationBase::isStandaloneApp()) ? 1.0 : scale; d.scale = masterScale * scale; diff --git a/libs/juce-current/source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h b/libs/juce-current/source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h index 5eb22f99..83e07677 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h +++ b/libs/juce-current/source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h @@ -109,7 +109,7 @@ public: BorderSize getBorderSize (::Window) const; Rectangle getWindowBounds (::Window, ::Window parentWindow); - Point getParentScreenPosition() const; + Point getPhysicalParentScreenPosition() const; bool contains (::Window, Point localPos) const; diff --git a/libs/juce-current/source/modules/juce_gui_basics/widgets/juce_Slider.cpp b/libs/juce-current/source/modules/juce_gui_basics/widgets/juce_Slider.cpp index 731193ff..0f68f6ef 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/widgets/juce_Slider.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/widgets/juce_Slider.cpp @@ -749,7 +749,7 @@ public: ? e.position.x - mouseDragStartPos.x : mouseDragStartPos.y - e.position.y; - newPos = owner.valueToProportionOfLength (valueOnMouseDown) + newPos = owner.valueToProportionOfLength (valueWhenLastDragged) + mouseDiff * (1.0 / pixelsForFullDragExtent); if (style == IncDecButtons) @@ -763,7 +763,7 @@ public: auto mouseDiff = (e.position.x - mouseDragStartPos.x) + (mouseDragStartPos.y - e.position.y); - newPos = owner.valueToProportionOfLength (valueOnMouseDown) + newPos = owner.valueToProportionOfLength (valueWhenLastDragged) + mouseDiff * (1.0 / pixelsForFullDragExtent); } else @@ -774,6 +774,7 @@ public: newPos = 1.0 - newPos; } + mouseDragStartPos = e.position; newPos = (isRotary() && ! rotaryParams.stopAtEnd) ? newPos - std::floor (newPos) : jlimit (0.0, 1.0, newPos); valueWhenLastDragged = owner.proportionOfLengthToValue (newPos); diff --git a/libs/juce-current/source/modules/juce_gui_basics/widgets/juce_TextEditor.cpp b/libs/juce-current/source/modules/juce_gui_basics/widgets/juce_TextEditor.cpp index 233c2330..70afc33a 100644 --- a/libs/juce-current/source/modules/juce_gui_basics/widgets/juce_TextEditor.cpp +++ b/libs/juce-current/source/modules/juce_gui_basics/widgets/juce_TextEditor.cpp @@ -833,6 +833,11 @@ struct TextEditor::TextHolderComponent : public Component, { owner.drawContent (g); } + + void setTopLeftPosition(Point new_position) override { + Component::setTopLeftPosition(new_position); + owner.textChanged(); + } void restartTimer() { @@ -1558,6 +1563,9 @@ void TextEditor::moveCaretTo (const int newPosition, const bool isSelecting) moveCaret (newPosition); selection = Range::emptyRange (getCaretPosition()); } + + if (listeners.size() != 0 || onTextChange != nullptr) + postCommandMessage (TextEditorDefs::textChangeMessageId); } int TextEditor::getTextIndexAt (const int x, const int y) @@ -2141,6 +2149,9 @@ void TextEditor::focusGained (FocusChangeType cause) repaint(); updateCaretPosition(); + + if (listeners.size() != 0 || onTextChange != nullptr) + postCommandMessage (TextEditorDefs::textChangeMessageId); } void TextEditor::focusLost (FocusChangeType) diff --git a/libs/juce-current/source/modules/juce_gui_extra/embedding/juce_ScopedDPIAwarenessDisabler.h b/libs/juce-current/source/modules/juce_gui_extra/embedding/juce_ScopedDPIAwarenessDisabler.h old mode 100644 new mode 100755 index cb9644b7..d3c88930 --- a/libs/juce-current/source/modules/juce_gui_extra/embedding/juce_ScopedDPIAwarenessDisabler.h +++ b/libs/juce-current/source/modules/juce_gui_extra/embedding/juce_ScopedDPIAwarenessDisabler.h @@ -26,8 +26,6 @@ namespace juce { -#if (JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE) || DOXYGEN - //============================================================================== /** A Windows-specific class that temporarily sets the DPI awareness context of @@ -52,6 +50,5 @@ public: private: void* previousContext = nullptr; }; -#endif } // namespace juce diff --git a/libs/juce-current/source/modules/juce_gui_extra/juce_gui_extra.cpp b/libs/juce-current/source/modules/juce_gui_extra/juce_gui_extra.cpp old mode 100644 new mode 100755 index d42555e1..d489fc2a --- a/libs/juce-current/source/modules/juce_gui_extra/juce_gui_extra.cpp +++ b/libs/juce-current/source/modules/juce_gui_extra/juce_gui_extra.cpp @@ -39,6 +39,7 @@ #define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1 #define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 #define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1 +#define JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER 1 #ifndef JUCE_PUSH_NOTIFICATIONS #define JUCE_PUSH_NOTIFICATIONS 0 @@ -192,3 +193,9 @@ #include "native/juce_android_WebBrowserComponent.cpp" #endif #endif + +//============================================================================== +#if ! JUCE_WINDOWS + juce::ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { ignoreUnused (previousContext); } + juce::ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() {} +#endif diff --git a/libs/juce-current/source/modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp b/libs/juce-current/source/modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp index 905e1378..7ab2dd56 100644 --- a/libs/juce-current/source/modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp +++ b/libs/juce-current/source/modules/juce_gui_extra/native/juce_win32_HWNDComponent.cpp @@ -26,8 +26,6 @@ namespace juce { -void setThreadDPIAwarenessForWindow (HWND); - class HWNDComponent::Pimpl : public ComponentMovementWatcher { public: @@ -52,13 +50,13 @@ public: { auto area = (peer->getAreaCoveredBy (owner).toFloat() * peer->getPlatformScaleFactor()).getSmallestIntegerContainer(); - setThreadDPIAwarenessForWindow (hwnd); - UINT flagsToSend = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER; if (! wasMoved) flagsToSend |= SWP_NOMOVE; if (! wasResized) flagsToSend |= SWP_NOSIZE; + ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { hwnd }; + SetWindowPos (hwnd, nullptr, area.getX(), area.getY(), area.getWidth(), area.getHeight(), flagsToSend); } } @@ -101,7 +99,7 @@ public: { if (auto* peer = owner.getPeer()) { - setThreadDPIAwarenessForWindow (hwnd); + ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { hwnd }; RECT r; GetWindowRect (hwnd, &r); diff --git a/libs/juce-current/source/modules/juce_opengl/juce_opengl.cpp b/libs/juce-current/source/modules/juce_opengl/juce_opengl.cpp index 76754ae0..b6b4e6da 100644 --- a/libs/juce-current/source/modules/juce_opengl/juce_opengl.cpp +++ b/libs/juce-current/source/modules/juce_opengl/juce_opengl.cpp @@ -37,6 +37,7 @@ #define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 #define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 #define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1 +#define JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER 1 #include "juce_opengl.h" diff --git a/libs/juce-current/source/modules/juce_opengl/juce_opengl.h b/libs/juce-current/source/modules/juce_opengl/juce_opengl.h index f7bc806f..d325d3e3 100644 --- a/libs/juce-current/source/modules/juce_opengl/juce_opengl.h +++ b/libs/juce-current/source/modules/juce_opengl/juce_opengl.h @@ -124,7 +124,7 @@ It's mandatory in OpenGL 3.0 to specify the GLSL version. */ #if JUCE_OPENGL3 - #if JUCE_OPENGL_ES + #if JUCE_OPENGL_ES || OPENGL_ES #define JUCE_GLSL_VERSION "#version 300 es" #else #define JUCE_GLSL_VERSION "#version 150" diff --git a/libs/juce-current/source/modules/juce_opengl/native/juce_MissingGLDefinitions.h b/libs/juce-current/source/modules/juce_opengl/native/juce_MissingGLDefinitions.h index c23ec097..00c6cf56 100644 --- a/libs/juce-current/source/modules/juce_opengl/native/juce_MissingGLDefinitions.h +++ b/libs/juce-current/source/modules/juce_opengl/native/juce_MissingGLDefinitions.h @@ -126,6 +126,14 @@ enum MissingOpenGLDefinitions GL_DYNAMIC_DRAW = 0x88E8, GL_STREAM_DRAW = 0x88E0, + GL_GEOMETRY_SHADER = 0x8DD9, + GL_LINE_STRIP_ADJACENCY = 0x000B, + GL_INTERLEAVED_ATTRIBS = 0x8C8C, + GL_STATIC_READ = 0x88E5, + GL_TRANSFORM_FEEDBACK_BUFFER = 0x8C8E, + GL_RASTERIZER_DISCARD = 0x8C89, + GL_MAP_READ_BIT = 0x0001, + WGL_NUMBER_PIXEL_FORMATS_ARB = 0x2000, WGL_DRAW_TO_WINDOW_ARB = 0x2001, WGL_ACCELERATION_ARB = 0x2003, diff --git a/libs/juce-current/source/modules/juce_opengl/native/juce_OpenGL_linux_X11.h b/libs/juce-current/source/modules/juce_opengl/native/juce_OpenGL_linux_X11.h index ef7eb852..b4ba85cc 100644 --- a/libs/juce-current/source/modules/juce_opengl/native/juce_OpenGL_linux_X11.h +++ b/libs/juce-current/source/modules/juce_opengl/native/juce_OpenGL_linux_X11.h @@ -69,8 +69,8 @@ public: GLint attribs[] = { - GLX_RGBA, - GLX_DOUBLEBUFFER, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DOUBLEBUFFER, True, GLX_RED_SIZE, cPixelFormat.redBits, GLX_GREEN_SIZE, cPixelFormat.greenBits, GLX_BLUE_SIZE, cPixelFormat.blueBits, @@ -81,13 +81,21 @@ public: GLX_ACCUM_GREEN_SIZE, cPixelFormat.accumulationBufferGreenBits, GLX_ACCUM_BLUE_SIZE, cPixelFormat.accumulationBufferBlueBits, GLX_ACCUM_ALPHA_SIZE, cPixelFormat.accumulationBufferAlphaBits, + GLX_X_RENDERABLE, True, None }; - bestVisual = glXChooseVisual (display, X11Symbols::getInstance()->xDefaultScreen (display), attribs); - if (bestVisual == nullptr) + int countFbConfigs; + fbConfig = glXChooseFBConfig (display, DefaultScreen (display), attribs, &countFbConfigs); + if (fbConfig == nullptr) return; + bestVisual = glXGetVisualFromFBConfig (display, *fbConfig); + if (bestVisual == nullptr) { + X11Symbols::getInstance()->xFree (fbConfig); + return; + } + auto* peer = component.getPeer(); jassert (peer != nullptr); @@ -139,6 +147,9 @@ public: } } + if (fbConfig != nullptr) + X11Symbols::getInstance()->xFree (fbConfig); + if (bestVisual != nullptr) X11Symbols::getInstance()->xFree (bestVisual); } @@ -146,7 +157,18 @@ public: bool initialiseOnRenderThread (OpenGLContext& c) { XWindowSystemUtilities::ScopedXLock xLock; - renderContext = glXCreateContext (display, bestVisual, (GLXContext) contextToShareWith, GL_TRUE); + PFNGLXCREATECONTEXTATTRIBSARBPROC createContextAttribs; + int attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 2, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + 0 + }; + + createContextAttribs = (PFNGLXCREATECONTEXTATTRIBSARBPROC) + OpenGLHelpers::getExtensionFunction("glXCreateContextAttribsARB"); + + renderContext = createContextAttribs (display, *fbConfig, (GLXContext) contextToShareWith, GL_TRUE, attribs); c.makeActive(); context = &c; @@ -240,6 +262,7 @@ private: int swapFrames = 1; Rectangle bounds; XVisualInfo* bestVisual = nullptr; + GLXFBConfig* fbConfig = nullptr; void* contextToShareWith; OpenGLContext* context = nullptr; diff --git a/libs/juce-current/source/modules/juce_opengl/native/juce_OpenGL_win32.h b/libs/juce-current/source/modules/juce_opengl/native/juce_OpenGL_win32.h index 6106fce5..f602d229 100644 --- a/libs/juce-current/source/modules/juce_opengl/native/juce_OpenGL_win32.h +++ b/libs/juce-current/source/modules/juce_opengl/native/juce_OpenGL_win32.h @@ -28,10 +28,6 @@ namespace juce extern ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component&, void* parent); -#if JUCE_WIN_PER_MONITOR_DPI_AWARE - extern void setThreadDPIAwarenessForWindow (HWND); -#endif - //============================================================================== class OpenGLContext::NativeContext #if JUCE_WIN_PER_MONITOR_DPI_AWARE @@ -97,15 +93,17 @@ public: bool initialiseOnRenderThread (OpenGLContext& c) { - #if JUCE_WIN_PER_MONITOR_DPI_AWARE - setThreadDPIAwarenessForWindow ((HWND) nativeWindow->getNativeHandle()); - #endif - + threadAwarenessSetter = std::make_unique (nativeWindow->getNativeHandle()); context = &c; return true; } - void shutdownOnRenderThread() { deactivateCurrentContext(); context = nullptr; } + void shutdownOnRenderThread() + { + deactivateCurrentContext(); + context = nullptr; + threadAwarenessSetter = nullptr; + } static void deactivateCurrentContext() { wglMakeCurrent (nullptr, nullptr); } bool makeActive() const noexcept { return isActive() || wglMakeCurrent (dc, renderContext) != FALSE; } @@ -170,6 +168,7 @@ private: std::unique_ptr dummyComponent; std::unique_ptr nativeWindow; + std::unique_ptr threadAwarenessSetter; HGLRC renderContext; HDC dc; OpenGLContext* context = {}; @@ -219,7 +218,13 @@ private: void createNativeWindow (Component& component) { auto* topComp = component.getTopLevelComponent(); - nativeWindow.reset (createNonRepaintingEmbeddedWindowsPeer (*dummyComponent, topComp->getWindowHandle())); + + { + auto* parentHWND = topComp->getWindowHandle(); + + ScopedThreadDPIAwarenessSetter setter { parentHWND }; + nativeWindow.reset (createNonRepaintingEmbeddedWindowsPeer (*dummyComponent, parentHWND)); + } if (auto* peer = topComp->getPeer()) { @@ -285,6 +290,8 @@ private: atts[n++] = WGL_DRAW_TO_WINDOW_ARB; atts[n++] = GL_TRUE; atts[n++] = WGL_SUPPORT_OPENGL_ARB; atts[n++] = GL_TRUE; + atts[n++] = WGL_CONTEXT_MAJOR_VERSION_ARB; atts[n++] = 3; + atts[n++] = WGL_CONTEXT_MINOR_VERSION_ARB; atts[n++] = 2; atts[n++] = WGL_DOUBLE_BUFFER_ARB; atts[n++] = GL_TRUE; atts[n++] = WGL_PIXEL_TYPE_ARB; atts[n++] = WGL_TYPE_RGBA_ARB; atts[n++] = WGL_ACCELERATION_ARB; diff --git a/libs/juce-current/source/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp b/libs/juce-current/source/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp index 97dc1f0b..ad40fa64 100644 --- a/libs/juce-current/source/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp +++ b/libs/juce-current/source/modules/juce_opengl/opengl/juce_OpenGLHelpers.cpp @@ -83,7 +83,7 @@ void OpenGLHelpers::enableScissorTest (Rectangle clip) String OpenGLHelpers::translateVertexShaderToV3 (const String& code) { - #if JUCE_OPENGL3 + #if JUCE_OPENGL3 || OPENGL_ES if (OpenGLShaderProgram::getLanguageVersion() > 1.2) { String output; @@ -119,7 +119,7 @@ String OpenGLHelpers::translateVertexShaderToV3 (const String& code) String OpenGLHelpers::translateFragmentShaderToV3 (const String& code) { - #if JUCE_OPENGL3 + #if JUCE_OPENGL3 || OPENGL_ES if (OpenGLShaderProgram::getLanguageVersion() > 1.2) return JUCE_GLSL_VERSION "\n" "out " JUCE_MEDIUMP " vec4 fragColor;\n"