Signed-off-by: falkTX <falktx@falktx.com>tags/2021-03-15
@@ -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); } | void operator() (LPWSTR ptr) const noexcept { CoTaskMemFree (ptr); } | ||||
}; | }; | ||||
+ #if JUCE_MSVC | + #if JUCE_MSVC | ||||
bool showDialog (IFileDialog& dialog, bool async) const | |||||
bool showDialog (IFileDialog& dialog, bool async) | |||||
{ | { | ||||
FILEOPENDIALOGOPTIONS flags = {}; | FILEOPENDIALOGOPTIONS flags = {}; | ||||
@@ -327,6 +328,7 @@ private: | |||||
@@ -327,6 +328,7 @@ private: | |||||
return result; | return result; | ||||
} | } | ||||
@@ -18,7 +18,7 @@ index 0c1138a2b..a5b42b8ef 100644 | |||||
Array<URL> openDialogPreVista (bool async) | Array<URL> openDialogPreVista (bool async) | ||||
{ | { | ||||
@@ -436,11 +438,13 @@ private: | |||||
@@ -436,11 +438,13 @@ private: | |||||
const Remover remover (*this); | const Remover remover (*this); | ||||
@@ -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 <typename PointOrRect> | template <typename PointOrRect> | ||||
static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p) | static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p) | ||||
{ | { | ||||
@@ -13,7 +13,7 @@ index c4404c637..fa8d3ccf5 100644 | |||||
while (source != nullptr) | while (source != nullptr) | ||||
{ | { | ||||
if (source == target) | if (source == target) | ||||
@@ -395,6 +399,9 @@ struct Component::ComponentHelpers | |||||
@@ -395,6 +399,9 @@ struct Component::ComponentHelpers | |||||
if (source->isParentOf (target)) | if (source->isParentOf (target)) | ||||
return convertFromDistantParentSpace (source, *target, p); | return convertFromDistantParentSpace (source, *target, p); | ||||
@@ -23,7 +23,7 @@ index c4404c637..fa8d3ccf5 100644 | |||||
p = convertToParentSpace (*source, p); | p = convertToParentSpace (*source, p); | ||||
source = source->getParentComponent(); | source = source->getParentComponent(); | ||||
} | } | ||||
@@ -1390,13 +1397,14 @@ bool Component::reallyContains (Point<int> point, bool returnTrueIfWithinAChild) | |||||
@@ -1390,13 +1397,14 @@ bool Component::reallyContains (Point<int> point, bool returnTrueIfWithinAChild) | |||||
Component* Component::getComponentAt (Point<int> position) | Component* Component::getComponentAt (Point<int> position) | ||||
{ | { | ||||
@@ -39,11 +39,11 @@ index c4404c637..fa8d3ccf5 100644 | |||||
if (child != nullptr) | if (child != nullptr) | ||||
return child; | 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; } | bool getViewportIgnoreDragFlag() const noexcept { return flags.viewportIgnoreDragFlag; } | ||||
@@ -61,11 +61,11 @@ index 6b2b0072b..ccb2681fa 100644 | |||||
private: | private: | ||||
//============================================================================== | //============================================================================== | ||||
friend class ComponentPeer; | 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()) | if (auto* peer = comp.getPeer()) | ||||
{ | { | ||||
@@ -74,11 +74,11 @@ index a8c2c283a..ddb15b88d 100644 | |||||
auto& peerComp = peer->getComponent(); | auto& peerComp = peer->getComponent(); | ||||
return comp.getLocalPoint (&peerComp, ScalingHelpers::unscaledScreenPosToScaled (peerComp, pos)); | 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)) | if (DragHelpers::isSuitableTarget (info, newTarget)) | ||||
{ | { | ||||
dragAndDropTargetComponent = newTarget; | dragAndDropTargetComponent = newTarget; | ||||
@@ -87,7 +87,7 @@ index 8d7febd4b..7ec8fbb00 100644 | |||||
if (DragHelpers::isFileDrag (info)) | if (DragHelpers::isFileDrag (info)) | ||||
dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragEnter (info.files, pos.x, pos.y); | dynamic_cast<FileDragAndDropTarget*> (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)) | if (! DragHelpers::isSuitableTarget (info, newTarget)) | ||||
return false; | return false; | ||||
@@ -96,11 +96,11 @@ index 8d7febd4b..7ec8fbb00 100644 | |||||
if (DragHelpers::isFileDrag (info)) | if (DragHelpers::isFileDrag (info)) | ||||
dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragMove (info.files, pos.x, pos.y); | dynamic_cast<FileDragAndDropTarget*> (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 (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 (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))\ | USE_FUNCTION (glFramebufferRenderbuffer, void, (GLenum p1, GLenum p2, GLenum p3, GLuint p4), (p1, p2, p3, p4))\ | ||||
@@ -42,7 +42,7 @@ void MidiKeyboardState::reset() | |||||
bool MidiKeyboardState::isNoteOn (const int midiChannel, const int n) const noexcept | 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) | return isPositiveAndBelow (n, 128) | ||||
&& (noteStates[n] & (1 << (midiChannel - 1))) != 0; | && (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) | 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)); | jassert (isPositiveAndBelow (midiNoteNumber, 128)); | ||||
const ScopedLock sl (lock); | const ScopedLock sl (lock); | ||||
@@ -36,7 +36,7 @@ bool MidiRPNDetector::parseControllerMessage (int midiChannel, | |||||
int controllerValue, | int controllerValue, | ||||
MidiRPNMessage& result) noexcept | MidiRPNMessage& result) noexcept | ||||
{ | { | ||||
jassert (midiChannel >= 1 && midiChannel <= 16); | |||||
jassert (midiChannel > 0 && midiChannel <= 16); | |||||
jassert (controllerNumber >= 0 && controllerNumber < 128); | jassert (controllerNumber >= 0 && controllerNumber < 128); | ||||
jassert (controllerValue >= 0 && controllerValue < 128); | jassert (controllerValue >= 0 && controllerValue < 128); | ||||
@@ -97,7 +97,7 @@ void MPEChannelAssigner::noteOff (int noteNumber, int midiChannel) | |||||
return false; | return false; | ||||
}; | }; | ||||
if (midiChannel >= 0 && midiChannel < 17) | |||||
if (midiChannel >= 0 && midiChannel <= 16) | |||||
{ | { | ||||
removeNote (midiChannels[midiChannel], noteNumber); | removeNote (midiChannels[midiChannel], noteNumber); | ||||
return; | return; | ||||
@@ -842,6 +842,26 @@ namespace WavFileHelpers | |||||
return out.getMemoryBlock(); | 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 | namespace AXMLChunk | ||||
@@ -1321,6 +1341,7 @@ public: | |||||
listInfoChunk = ListInfoChunk::createFrom (metadataValues); | listInfoChunk = ListInfoChunk::createFrom (metadataValues); | ||||
acidChunk = AcidChunk::createFrom (metadataValues); | acidChunk = AcidChunk::createFrom (metadataValues); | ||||
trckChunk = TracktionChunk::createFrom (metadataValues); | trckChunk = TracktionChunk::createFrom (metadataValues); | ||||
clm_Chunk = Clm_Chunk::createFrom (metadataValues); | |||||
} | } | ||||
headerPosition = out->getPosition(); | headerPosition = out->getPosition(); | ||||
@@ -1383,7 +1404,7 @@ public: | |||||
} | } | ||||
private: | 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; | uint64 lengthInSamples = 0, bytesWritten = 0; | ||||
int64 headerPosition = 0; | int64 headerPosition = 0; | ||||
bool writeFailed = false; | bool writeFailed = false; | ||||
@@ -1421,6 +1442,7 @@ private: | |||||
+ chunkSize (listInfoChunk) | + chunkSize (listInfoChunk) | ||||
+ chunkSize (acidChunk) | + chunkSize (acidChunk) | ||||
+ chunkSize (trckChunk) | + chunkSize (trckChunk) | ||||
+ chunkSize (clm_Chunk) | |||||
+ (8 + 28)); // (ds64 chunk) | + (8 + 28)); // (ds64 chunk) | ||||
riffChunkSize += (riffChunkSize & 1); | riffChunkSize += (riffChunkSize & 1); | ||||
@@ -1503,6 +1525,7 @@ private: | |||||
writeChunk (listInfoChunk, chunkName ("LIST")); | writeChunk (listInfoChunk, chunkName ("LIST")); | ||||
writeChunk (acidChunk, chunkName ("acid")); | writeChunk (acidChunk, chunkName ("acid")); | ||||
writeChunk (trckChunk, chunkName ("Trkn")); | writeChunk (trckChunk, chunkName ("Trkn")); | ||||
writeChunk (clm_Chunk, chunkName ("clm ")); | |||||
writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); | writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); | ||||
@@ -1755,6 +1755,9 @@ private: | |||||
Array<const AudioProcessorParameterGroup*> parameterGroups; | Array<const AudioProcessorParameterGroup*> parameterGroups; | ||||
//============================================================================== | //============================================================================== | ||||
// According to the docs, this is the maximum size of a MIDIPacketList. | |||||
static constexpr UInt32 packetListBytes = 65536; | |||||
AudioUnitEvent auEvent; | AudioUnitEvent auEvent; | ||||
mutable Array<AUPreset> presetsArray; | mutable Array<AUPreset> presetsArray; | ||||
CriticalSection incomingMidiLock; | CriticalSection incomingMidiLock; | ||||
@@ -1762,6 +1765,7 @@ private: | |||||
AudioTimeStamp lastTimeStamp; | AudioTimeStamp lastTimeStamp; | ||||
int totalInChannels, totalOutChannels; | int totalInChannels, totalOutChannels; | ||||
HeapBlock<bool> pulledSucceeded; | HeapBlock<bool> pulledSucceeded; | ||||
HeapBlock<MIDIPacketList> packetList { packetListBytes, 1 }; | |||||
ThreadLocalValue<bool> inParameterChangedCallback; | ThreadLocalValue<bool> inParameterChangedCallback; | ||||
@@ -1858,37 +1862,55 @@ private: | |||||
void pushMidiOutput (UInt32 nFrames) noexcept | 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<MIDIPacketList> packetList; | |||||
packetList.malloc (packetListMembersSize + packetMembersSize * numPackets + dataSize, 1); | |||||
packetList->numPackets = numPackets; | |||||
const auto add = [&] (const MidiMessageMetadata& metadata) | |||||
{ | |||||
end = MIDIPacketListAdd (packetList, | |||||
packetListBytes, | |||||
end, | |||||
static_cast<MIDITimeStamp> (metadata.samplePosition), | |||||
static_cast<ByteCount> (metadata.numBytes), | |||||
metadata.data); | |||||
}; | |||||
p = packetList->packet; | |||||
init(); | |||||
for (const auto metadata : midiEvents) | 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) | void GetAudioBufferList (bool isInput, int busIdx, AudioBufferList*& bufferList, bool& interleaved, int& numChannels) | ||||
@@ -1158,11 +1158,6 @@ public: | |||||
{ | { | ||||
auto editorBounds = getSizeToContainChild(); | auto editorBounds = getSizeToContainChild(); | ||||
#if JUCE_MAC | |||||
if (wrapper.useNSView) | |||||
setTopLeftPosition (0, getHeight() - editorBounds.getHeight()); | |||||
#endif | |||||
resizeHostWindow (editorBounds.getWidth(), editorBounds.getHeight()); | resizeHostWindow (editorBounds.getWidth(), editorBounds.getHeight()); | ||||
{ | { | ||||
@@ -28,6 +28,9 @@ | |||||
//============================================================================== | //============================================================================== | ||||
#if JucePlugin_Build_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX) | #if JucePlugin_Build_VST3 && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX) | ||||
#if JUCE_LINUX^M | |||||
#include <arpa/inet.h>^M | |||||
#endif^M | |||||
#if JUCE_PLUGINHOST_VST3 | #if JUCE_PLUGINHOST_VST3 | ||||
#if JUCE_MAC | #if JUCE_MAC | ||||
@@ -2618,6 +2621,9 @@ public: | |||||
//============================================================================== | //============================================================================== | ||||
void processParameterChanges (Vst::IParameterChanges& paramChanges) | void processParameterChanges (Vst::IParameterChanges& paramChanges) | ||||
{ | { | ||||
if (juceVST3EditController == nullptr) | |||||
return; | |||||
jassert (pluginInstance != nullptr); | jassert (pluginInstance != nullptr); | ||||
auto numParamsChanged = paramChanges.getParameterCount(); | auto numParamsChanged = paramChanges.getParameterCount(); | ||||
@@ -78,9 +78,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4355) | |||||
//============================================================================== | //============================================================================== | ||||
namespace juce | namespace juce | ||||
{ | { | ||||
#if JUCE_WINDOWS | |||||
extern void setThreadDPIAwarenessForWindow (HWND); | |||||
#endif | |||||
//============================================================================== | //============================================================================== | ||||
namespace | namespace | ||||
@@ -2848,38 +2845,30 @@ public: | |||||
if (recursiveResize) | if (recursiveResize) | ||||
return; | return; | ||||
auto* topComp = getTopLevelComponent(); | |||||
if (topComp->getPeer() != nullptr) | |||||
if (auto* peer = getTopLevelComponent()->getPeer()) | |||||
{ | { | ||||
auto pos = (topComp->getLocalPoint (this, Point<int>()) * nativeScaleFactor).roundToInt(); | |||||
const ScopedValueSetter<bool> recursiveResizeSetter (recursiveResize, true); | |||||
recursiveResize = true; | |||||
auto pos = (peer->getAreaCoveredBy (*this).toFloat() * nativeScaleFactor).toNearestInt(); | |||||
#if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
if (pluginHWND != 0) | 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 | #elif JUCE_LINUX | ||||
if (pluginWindow != 0) | if (pluginWindow != 0) | ||||
{ | { | ||||
X11Symbols::getInstance()->xMoveResizeWindow (display, pluginWindow, | X11Symbols::getInstance()->xMoveResizeWindow (display, pluginWindow, | ||||
pos.getX(), pos.getY(), | pos.getX(), pos.getY(), | ||||
static_cast<unsigned int> (roundToInt ((float) getWidth() * nativeScaleFactor)), | |||||
static_cast<unsigned int> (roundToInt ((float) getHeight() * nativeScaleFactor))); | |||||
(unsigned int) pos.getWidth(), | |||||
(unsigned int) pos.getHeight()); | |||||
X11Symbols::getInstance()->xMapRaised (display, pluginWindow); | X11Symbols::getInstance()->xMapRaised (display, pluginWindow); | ||||
X11Symbols::getInstance()->xFlush (display); | X11Symbols::getInstance()->xFlush (display); | ||||
} | } | ||||
#endif | #endif | ||||
recursiveResize = false; | |||||
} | } | ||||
} | } | ||||
@@ -3107,7 +3096,12 @@ private: | |||||
JUCE_END_IGNORE_WARNINGS_MSVC | JUCE_END_IGNORE_WARNINGS_MSVC | ||||
RECT r; | RECT r; | ||||
GetWindowRect (pluginHWND, &r); | |||||
{ | |||||
ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { pluginHWND }; | |||||
GetWindowRect (pluginHWND, &r); | |||||
} | |||||
auto w = (int) (r.right - r.left); | auto w = (int) (r.right - r.left); | ||||
auto h = (int) (r.bottom - r.top); | auto h = (int) (r.bottom - r.top); | ||||
@@ -3122,7 +3116,7 @@ private: | |||||
// very dodgy logic to decide which size is right. | // very dodgy logic to decide which size is right. | ||||
if (std::abs (rw - w) > 350 || std::abs (rh - h) > 350) | if (std::abs (rw - w) > 350 || std::abs (rh - h) > 350) | ||||
{ | { | ||||
setThreadDPIAwarenessForWindow (pluginHWND); | |||||
ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { pluginHWND }; | |||||
SetWindowPos (pluginHWND, 0, | SetWindowPos (pluginHWND, 0, | ||||
0, 0, roundToInt (rw * nativeScaleFactor), roundToInt (rh * nativeScaleFactor), | 0, 0, roundToInt (rw * nativeScaleFactor), roundToInt (rh * nativeScaleFactor), | ||||
@@ -35,6 +35,7 @@ | |||||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | #define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | ||||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | #define JUCE_CORE_INCLUDE_OBJC_HELPERS 1 | ||||
#define JUCE_GUI_BASICS_INCLUDE_XHEADERS 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 "juce_audio_processors.h" | ||||
#include <juce_gui_extra/juce_gui_extra.h> | #include <juce_gui_extra/juce_gui_extra.h> | ||||
@@ -54,38 +54,24 @@ void AudioProcessorEditor::hostMIDIControllerIsAvailable (bool) { | |||||
void AudioProcessorEditor::initialise() | void AudioProcessorEditor::initialise() | ||||
{ | { | ||||
resizable = false; | |||||
attachConstrainer (&defaultConstrainer); | |||||
setConstrainer (&defaultConstrainer); | |||||
resizeListener.reset (new AudioProcessorEditorListener (*this)); | resizeListener.reset (new AudioProcessorEditorListener (*this)); | ||||
addComponentListener (resizeListener.get()); | 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(); | attachResizableCornerComponent(); | ||||
else | else | ||||
resizableCorner.reset(); | |||||
resizableCorner = nullptr; | |||||
} | } | ||||
} | } | ||||
@@ -94,19 +80,23 @@ void AudioProcessorEditor::setResizeLimits (int newMinimumWidth, | |||||
int newMaximumWidth, | int newMaximumWidth, | ||||
int newMaximumHeight) noexcept | 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) | if (constrainer == nullptr) | ||||
setConstrainer (&defaultConstrainer); | setConstrainer (&defaultConstrainer); | ||||
defaultConstrainer.setSizeLimits (newMinimumWidth, newMinimumHeight, | |||||
newMaximumWidth, newMaximumHeight); | |||||
if (resizableCorner != nullptr) | |||||
attachResizableCornerComponent(); | |||||
setBoundsConstrained (getBounds()); | setBoundsConstrained (getBounds()); | ||||
} | } | ||||
@@ -115,29 +105,21 @@ void AudioProcessorEditor::setConstrainer (ComponentBoundsConstrainer* newConstr | |||||
{ | { | ||||
if (constrainer != newConstrainer) | 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) | if (resizableCorner != nullptr) | ||||
attachResizableCornerComponent(); | attachResizableCornerComponent(); | ||||
} | } | ||||
} | } | ||||
void AudioProcessorEditor::attachConstrainer (ComponentBoundsConstrainer* newConstrainer) | |||||
{ | |||||
if (constrainer != newConstrainer) | |||||
{ | |||||
constrainer = newConstrainer; | |||||
updatePeer(); | |||||
} | |||||
} | |||||
void AudioProcessorEditor::attachResizableCornerComponent() | void AudioProcessorEditor::attachResizableCornerComponent() | ||||
{ | { | ||||
resizableCorner.reset (new ResizableCornerComponent (this, constrainer)); | |||||
resizableCorner = std::make_unique<ResizableCornerComponent> (this, constrainer); | |||||
Component::addChildComponent (resizableCorner.get()); | Component::addChildComponent (resizableCorner.get()); | ||||
resizableCorner->setAlwaysOnTop (true); | resizableCorner->setAlwaysOnTop (true); | ||||
editorResized (true); | editorResized (true); | ||||
@@ -175,11 +157,6 @@ void AudioProcessorEditor::editorResized (bool wasResized) | |||||
getHeight() - resizerSize, | getHeight() - resizerSize, | ||||
resizerSize, resizerSize); | resizerSize, resizerSize); | ||||
} | } | ||||
if (! resizable) | |||||
if (auto w = getWidth()) | |||||
if (auto h = getHeight()) | |||||
defaultConstrainer.setSizeLimits (w, h, w, h); | |||||
} | } | ||||
} | } | ||||
@@ -54,12 +54,12 @@ public: | |||||
/** Destructor. */ | /** Destructor. */ | ||||
~AudioProcessorEditor() override; | ~AudioProcessorEditor() override; | ||||
//============================================================================== | //============================================================================== | ||||
/** The AudioProcessor that this editor represents. */ | /** The AudioProcessor that this editor represents. */ | ||||
AudioProcessor& processor; | AudioProcessor& processor; | ||||
/** Returns a pointer to the processor that this editor represents. | /** 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 | 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. | 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 | /** Some types of plugin can call this to suggest that the control for a particular | ||||
parameter should be highlighted. | parameter should be highlighted. | ||||
Currently only AAX plugins will call this, and implementing it is optional. | Currently only AAX plugins will call this, and implementing it is optional. | ||||
*/ | */ | ||||
virtual void setControlHighlight (ParameterControlHighlightInfo); | virtual void setControlHighlight (ParameterControlHighlightInfo); | ||||
@@ -117,36 +118,45 @@ public: | |||||
virtual void setScaleFactor (float newScale); | 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 | @see setResizeLimits, isResizable | ||||
*/ | */ | ||||
void setResizable (bool allowHostToResize, bool useBottomRightCornerResizer); | 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 | @see setResizable | ||||
*/ | */ | ||||
bool isResizable() const noexcept { return resizable; } | |||||
bool isResizable() const noexcept { return resizableByHost; } | |||||
/** This sets the maximum and minimum sizes for the window. | /** 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 | If the window's current size is outside these limits, it will be resized to | ||||
make sure it's within them. | 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 | 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 | window is dragged by the user or resized by other indirect means, the constrainer | ||||
will limit the numbers involved. | 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 | @see setResizable | ||||
*/ | */ | ||||
void setResizeLimits (int newMinimumWidth, | void setResizeLimits (int newMinimumWidth, | ||||
@@ -154,8 +164,8 @@ public: | |||||
int newMaximumWidth, | int newMaximumWidth, | ||||
int newMaximumHeight) noexcept; | int newMaximumHeight) noexcept; | ||||
/** Returns the bounds constrainer object that this window is using. | /** Returns the bounds constrainer object that this window is using. | ||||
You can access this to change its properties. | You can access this to change its properties. | ||||
*/ | */ | ||||
ComponentBoundsConstrainer* getConstrainer() noexcept { return constrainer; } | ComponentBoundsConstrainer* getConstrainer() noexcept { return constrainer; } | ||||
@@ -176,11 +186,14 @@ public: | |||||
*/ | */ | ||||
void setBoundsConstrained (Rectangle<int> newBounds); | void setBoundsConstrained (Rectangle<int> newBounds); | ||||
/** The ResizableCornerComponent which is currently being used by this editor, | |||||
or nullptr if it does not have one. | |||||
*/ | |||||
std::unique_ptr<ResizableCornerComponent> resizableCorner; | std::unique_ptr<ResizableCornerComponent> resizableCorner; | ||||
private: | private: | ||||
//============================================================================== | //============================================================================== | ||||
struct AudioProcessorEditorListener : ComponentListener | |||||
struct AudioProcessorEditorListener : public ComponentListener | |||||
{ | { | ||||
AudioProcessorEditorListener (AudioProcessorEditor& e) : ed (e) {} | AudioProcessorEditorListener (AudioProcessorEditor& e) : ed (e) {} | ||||
@@ -198,14 +211,13 @@ private: | |||||
void initialise(); | void initialise(); | ||||
void editorResized (bool wasResized); | void editorResized (bool wasResized); | ||||
void updatePeer(); | void updatePeer(); | ||||
void attachConstrainer (ComponentBoundsConstrainer*); | |||||
void attachResizableCornerComponent(); | void attachResizableCornerComponent(); | ||||
//============================================================================== | //============================================================================== | ||||
std::unique_ptr<AudioProcessorEditorListener> resizeListener; | std::unique_ptr<AudioProcessorEditorListener> resizeListener; | ||||
bool resizable; | |||||
bool resizableByHost = false; | |||||
ComponentBoundsConstrainer defaultConstrainer; | ComponentBoundsConstrainer defaultConstrainer; | ||||
ComponentBoundsConstrainer* constrainer = {}; | |||||
ComponentBoundsConstrainer* constrainer = nullptr; | |||||
AffineTransform hostScaleTransform; | AffineTransform hostScaleTransform; | ||||
JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor) | JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor) | ||||
@@ -393,6 +393,7 @@ public: | |||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, | ||||
TRANS("Error when trying to open audio device!"), | TRANS("Error when trying to open audio device!"), | ||||
error); | error); | ||||
resized(); | |||||
} | } | ||||
bool showDeviceControlPanel() | bool showDeviceControlPanel() | ||||
@@ -1120,6 +1121,8 @@ void AudioDeviceSelectorComponent::updateMidiOutput() | |||||
deviceManager.setDefaultMidiOutputDevice ({}); | deviceManager.setDefaultMidiOutputDevice ({}); | ||||
else | else | ||||
deviceManager.setDefaultMidiOutputDevice (currentMidiOutputs[selectedId - 1].identifier); | deviceManager.setDefaultMidiOutputDevice (currentMidiOutputs[selectedId - 1].identifier); | ||||
resized(); | |||||
} | } | ||||
void AudioDeviceSelectorComponent::changeListenerCallback (ChangeBroadcaster*) | void AudioDeviceSelectorComponent::changeListenerCallback (ChangeBroadcaster*) | ||||
@@ -332,6 +332,9 @@ public: | |||||
// or 3) data is in the in buffer | // or 3) data is in the in buffer | ||||
while ((! finished) && curlBuffer.getSize() == 0) | while ((! finished) && curlBuffer.getSize() == 0) | ||||
{ | { | ||||
if (Thread::currentThreadShouldExit()) | |||||
return false; | |||||
{ | { | ||||
const ScopedLock lock (cleanupLock); | const ScopedLock lock (cleanupLock); | ||||
@@ -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", | for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", | ||||
"google-chrome", "chromium-browser", "opera", "konqueror" }) | "google-chrome", "chromium-browser", "opera", "konqueror" }) | ||||
{ | { | ||||
cmdLines.add (String (browserName) + " " + cmdString.trim()); | |||||
cmdLines.add (String (browserName) + " " + cmdString.trim().quoted()); | |||||
} | } | ||||
cmdString = cmdLines.joinIntoString (" || "); | cmdString = cmdLines.joinIntoString (" || "); | ||||
@@ -186,7 +186,8 @@ namespace juce | |||||
#define JUCE_STRINGIFY(item) JUCE_STRINGIFY_MACRO_HELPER (item) | #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 | For example, instead of | ||||
@code | @code | ||||
@@ -214,6 +215,13 @@ namespace juce | |||||
className (const className&) = delete;\ | className (const className&) = delete;\ | ||||
className& operator= (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 | /** This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and | ||||
JUCE_LEAK_DETECTOR macro for a class. | JUCE_LEAK_DETECTOR macro for a class. | ||||
*/ | */ | ||||
@@ -74,6 +74,7 @@ struct SIMDNativeOps<float> | |||||
static forcedinline __m128 JUCE_VECTOR_CALLTYPE add (__m128 a, __m128 b) noexcept { return _mm_add_ps (a, b); } | 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 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 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_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_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); } | 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<double> | |||||
static forcedinline __m128d JUCE_VECTOR_CALLTYPE add (__m128d a, __m128d b) noexcept { return _mm_add_pd (a, b); } | 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 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 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_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_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); } | static forcedinline __m128d JUCE_VECTOR_CALLTYPE bit_xor (__m128d a, __m128d b) noexcept { return _mm_xor_pd (a, b); } | ||||
@@ -33,300 +33,302 @@ using MenuTrackingChangedCallback = void (*)(bool); | |||||
MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr; | MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr; | ||||
//============================================================================== | //============================================================================== | ||||
struct AppDelegate | |||||
struct AppDelegateClass : public ObjCClass<NSObject> | |||||
{ | { | ||||
public: | |||||
AppDelegate() | |||||
AppDelegateClass() : ObjCClass<NSObject> ("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<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> ("pushNotificationsDelegate"); | |||||
addMethod (@selector (applicationDidFinishLaunching:), applicationDidFinishLaunching, "v@:@"); | |||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") | 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 | 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<NSObject> | |||||
static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename) | |||||
{ | { | ||||
AppDelegateClass() : ObjCClass<NSObject> ("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<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> ("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<NSApplicationDelegate, NSUserNotificationCenterDelegate>* delegate) | |||||
{ | |||||
object_setInstanceVariable (self, "pushNotificationsDelegate", delegate); | |||||
} | |||||
for (NSString* f in filenames) | |||||
files.add (quotedIfContainsSpaces (f)); | |||||
static NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>* getPushNotificationsDelegate (id self) | |||||
{ | |||||
return getIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> (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<NSApplicationDelegate, NSUserNotificationCenterDelegate>* 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<NSApplicationDelegate, NSUserNotificationCenterDelegate>* getPushNotificationsDelegate (id self) | |||||
{ | |||||
return getIvar<NSObject<NSApplicationDelegate, NSUserNotificationCenterDelegate>*> (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() | static void shutdownNSApp() | ||||
{ | { | ||||
[NSApp stop: nil]; | [NSApp stop: nil]; | ||||
[NSEvent stopPeriodicEvents]; | |||||
[NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1]; | [NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1]; | ||||
} | } | ||||
@@ -482,10 +482,7 @@ void Button::mouseDrag (const MouseEvent& e) | |||||
bool Button::isMouseSourceOver (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) | void Button::focusGained (FocusChangeType) | ||||
@@ -76,9 +76,6 @@ void ShapeButton::setShape (const Path& newShape, | |||||
shape = newShape; | shape = newShape; | ||||
maintainShapeProportions = maintainShapeProportions_; | maintainShapeProportions = maintainShapeProportions_; | ||||
shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, Point<int>())); | |||||
setComponentEffect (hasShadow ? &shadow : nullptr); | |||||
if (resizeNowToFitThisShape) | if (resizeNowToFitThisShape) | ||||
{ | { | ||||
auto newBounds = shape.getBounds(); | auto newBounds = shape.getBounds(); | ||||
@@ -88,6 +88,8 @@ public: | |||||
Colour overColourOn, | Colour overColourOn, | ||||
Colour downColourOn); | 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'. | /** 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 | 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. | used to provide a different set of colours. | ||||
@@ -112,9 +114,8 @@ public: | |||||
private: | private: | ||||
//============================================================================== | //============================================================================== | ||||
Colour normalColour, overColour, downColour, | Colour normalColour, overColour, downColour, | ||||
normalColourOn, overColourOn, downColourOn, outlineColour; | |||||
normalColourOn, overColourOn, downColourOn, outlineColour, shadowColour; | |||||
bool useOnColours; | bool useOnColours; | ||||
DropShadowEffect shadow; | |||||
Path shape; | Path shape; | ||||
BorderSize<int> border; | BorderSize<int> border; | ||||
bool maintainShapeProportions; | bool maintainShapeProportions; | ||||
@@ -3021,7 +3021,8 @@ void Component::modifierKeysChanged (const ModifierKeys& modifiers) | |||||
void Component::internalModifierKeysChanged() | void Component::internalModifierKeysChanged() | ||||
{ | { | ||||
sendFakeMouseMove(); | |||||
auto mainMouse = Desktop::getInstance().getMainMouseSource(); | |||||
mainMouse.triggerFakeMove(); | |||||
modifierKeysChanged (ModifierKeys::currentModifiers); | modifierKeysChanged (ModifierKeys::currentModifiers); | ||||
} | } | ||||
@@ -387,6 +387,10 @@ struct Component::ComponentHelpers | |||||
template <typename PointOrRect> | template <typename PointOrRect> | ||||
static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p) | 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) | while (source != nullptr) | ||||
{ | { | ||||
if (source == target) | if (source == target) | ||||
@@ -395,6 +399,9 @@ struct Component::ComponentHelpers | |||||
if (source->isParentOf (target)) | if (source->isParentOf (target)) | ||||
return convertFromDistantParentSpace (source, *target, p); | return convertFromDistantParentSpace (source, *target, p); | ||||
if (source == top) | |||||
p /= total_scaling; | |||||
p = convertToParentSpace (*source, p); | p = convertToParentSpace (*source, p); | ||||
source = source->getParentComponent(); | source = source->getParentComponent(); | ||||
} | } | ||||
@@ -1390,13 +1397,14 @@ bool Component::reallyContains (Point<int> point, bool returnTrueIfWithinAChild) | |||||
Component* Component::getComponentAt (Point<int> position) | Component* Component::getComponentAt (Point<int> position) | ||||
{ | { | ||||
Point<int> scale = (position.toFloat() * getPixelScaling()).roundToInt(); | |||||
if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position)) | if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position)) | ||||
{ | { | ||||
for (int i = childComponentList.size(); --i >= 0;) | for (int i = childComponentList.size(); --i >= 0;) | ||||
{ | { | ||||
auto* child = childComponentList.getUnchecked(i); | auto* child = childComponentList.getUnchecked(i); | ||||
child = child->getComponentAt (ComponentHelpers::convertFromParentSpace (*child, position)); | |||||
child = child->getComponentAt (ComponentHelpers::convertFromParentSpace (*child, scale)); | |||||
if (child != nullptr) | if (child != nullptr) | ||||
return child; | return child; | ||||
@@ -439,7 +439,7 @@ public: | |||||
@see setBounds, ComponentListener::componentMovedOrResized | @see setBounds, ComponentListener::componentMovedOrResized | ||||
*/ | */ | ||||
void setTopLeftPosition (Point<int> newTopLeftPosition); | |||||
virtual void setTopLeftPosition (Point<int> newTopLeftPosition); | |||||
/** Moves the component to a new position. | /** Moves the component to a new position. | ||||
@@ -2179,10 +2179,7 @@ public: | |||||
operator ComponentType*() const noexcept { return getComponent(); } | operator ComponentType*() const noexcept { return getComponent(); } | ||||
/** Returns the component that this pointer refers to, or null if the component no longer exists. */ | /** 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. */ | /** If the component is valid, this deletes it and sets this pointer to null. */ | ||||
void deleteAndZero() { delete getComponent(); } | void deleteAndZero() { delete getComponent(); } | ||||
@@ -158,7 +158,7 @@ bool FileChooser::showDialog (const int flags, FilePreviewComponent* const previ | |||||
{ | { | ||||
FocusRestorer focusRestorer; | FocusRestorer focusRestorer; | ||||
pimpl.reset (createPimpl (flags, previewComp)); | |||||
pimpl = createPimpl (flags, previewComp); | |||||
pimpl->runModally(); | pimpl->runModally(); | ||||
// ensure that the finished function was invoked | // ensure that the finished function was invoked | ||||
@@ -179,12 +179,12 @@ void FileChooser::launchAsync (int flags, std::function<void (const FileChooser& | |||||
asyncCallback = std::move (callback); | asyncCallback = std::move (callback); | ||||
pimpl.reset (createPimpl (flags, previewComp)); | |||||
pimpl = createPimpl (flags, previewComp); | |||||
pimpl->launch(); | pimpl->launch(); | ||||
} | } | ||||
FileChooser::Pimpl* FileChooser::createPimpl (int flags, FilePreviewComponent* previewComp) | |||||
std::shared_ptr<FileChooser::Pimpl> FileChooser::createPimpl (int flags, FilePreviewComponent* previewComp) | |||||
{ | { | ||||
results.clear(); | results.clear(); | ||||
@@ -214,10 +214,8 @@ FileChooser::Pimpl* FileChooser::createPimpl (int flags, FilePreviewComponent* p | |||||
{ | { | ||||
return showPlatformDialog (*this, flags, previewComp); | return showPlatformDialog (*this, flags, previewComp); | ||||
} | } | ||||
else | |||||
{ | |||||
return new NonNative (*this, flags, previewComp); | |||||
} | |||||
return std::make_unique<NonNative> (*this, flags, previewComp); | |||||
} | } | ||||
Array<File> FileChooser::getResults() const noexcept | Array<File> FileChooser::getResults() const noexcept | ||||
@@ -325,12 +325,11 @@ private: | |||||
virtual void runModally() = 0; | virtual void runModally() = 0; | ||||
}; | }; | ||||
std::unique_ptr<Pimpl> pimpl; | |||||
std::shared_ptr<Pimpl> pimpl; | |||||
//============================================================================== | //============================================================================== | ||||
Pimpl* createPimpl (int, FilePreviewComponent*); | |||||
static Pimpl* showPlatformDialog (FileChooser&, int, | |||||
FilePreviewComponent*); | |||||
std::shared_ptr<Pimpl> createPimpl (int, FilePreviewComponent*); | |||||
static std::shared_ptr<Pimpl> showPlatformDialog (FileChooser&, int, FilePreviewComponent*); | |||||
class NonNative; | class NonNative; | ||||
friend class NonNative; | friend class NonNative; | ||||
@@ -41,6 +41,7 @@ | |||||
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1 | #define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1 | ||||
#define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 | #define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 | ||||
#define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1 | #define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1 | ||||
#define JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER 1 | |||||
#include "juce_gui_basics.h" | #include "juce_gui_basics.h" | ||||
@@ -341,6 +341,10 @@ namespace juce | |||||
#endif | #endif | ||||
#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_FlexItem.h" | ||||
#include "layout/juce_FlexBox.h" | #include "layout/juce_FlexBox.h" | ||||
@@ -43,14 +43,9 @@ void CaretComponent::paint (Graphics& g) | |||||
g.fillRect (getLocalBounds()); | g.fillRect (getLocalBounds()); | ||||
} | } | ||||
void CaretComponent::timerCallback() | |||||
{ | |||||
setVisible (shouldBeShown() && ! isVisible()); | |||||
} | |||||
void CaretComponent::setCaretPosition (const Rectangle<int>& characterArea) | void CaretComponent::setCaretPosition (const Rectangle<int>& characterArea) | ||||
{ | { | ||||
startTimer (380); | |||||
setVisible (shouldBeShown()); | setVisible (shouldBeShown()); | ||||
setBounds (characterArea.withWidth (2)); | setBounds (characterArea.withWidth (2)); | ||||
} | } | ||||
@@ -31,8 +31,7 @@ namespace juce | |||||
@tags{GUI} | @tags{GUI} | ||||
*/ | */ | ||||
class JUCE_API CaretComponent : public Component, | |||||
private Timer | |||||
class JUCE_API CaretComponent : public Component | |||||
{ | { | ||||
public: | public: | ||||
//============================================================================== | //============================================================================== | ||||
@@ -73,7 +72,6 @@ private: | |||||
Component* owner; | Component* owner; | ||||
bool shouldBeShown() const; | bool shouldBeShown() const; | ||||
void timerCallback() override; | |||||
JUCE_DECLARE_NON_COPYABLE (CaretComponent) | JUCE_DECLARE_NON_COPYABLE (CaretComponent) | ||||
}; | }; | ||||
@@ -271,23 +271,6 @@ void ComponentBoundsConstrainer::checkBounds (Rectangle<int>& bounds, | |||||
bounds.setWidth (roundToInt (bounds.getHeight() * aspectRatio)); | 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()); | jassert (! bounds.isEmpty()); | ||||
@@ -41,8 +41,6 @@ LookAndFeel_V1::LookAndFeel_V1() | |||||
setColour (PopupMenu::highlightedBackgroundColourId, Colour (0xbfa4c2ce)); | setColour (PopupMenu::highlightedBackgroundColourId, Colour (0xbfa4c2ce)); | ||||
setColour (PopupMenu::highlightedTextColourId, Colours::black); | setColour (PopupMenu::highlightedTextColourId, Colours::black); | ||||
setColour (TextEditor::focusedOutlineColourId, findColour (TextButton::buttonColourId)); | setColour (TextEditor::focusedOutlineColourId, findColour (TextButton::buttonColourId)); | ||||
scrollbarShadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 2, Point<int>())); | |||||
} | } | ||||
LookAndFeel_V1::~LookAndFeel_V1() | LookAndFeel_V1::~LookAndFeel_V1() | ||||
@@ -291,7 +289,7 @@ void LookAndFeel_V1::drawScrollbar (Graphics& g, ScrollBar& bar, | |||||
ImageEffectFilter* LookAndFeel_V1::getScrollbarEffect() | 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&) | ImageEffectFilter* LookAndFeel_V1::getSliderEffect (Slider&) | ||||
{ | { | ||||
return &scrollbarShadow; | |||||
return nullptr; | |||||
} | } | ||||
int LookAndFeel_V1::getSliderThumbRadius (Slider&) | int LookAndFeel_V1::getSliderThumbRadius (Slider&) | ||||
@@ -97,8 +97,6 @@ public: | |||||
bool positionTitleBarButtonsOnLeft) override; | bool positionTitleBarButtonsOnLeft) override; | ||||
private: | private: | ||||
DropShadowEffect scrollbarShadow; | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookAndFeel_V1) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookAndFeel_V1) | ||||
}; | }; | ||||
@@ -1292,8 +1292,6 @@ void LookAndFeel_V4::drawCallOutBoxBackground (CallOutBox& box, Graphics& g, | |||||
{ | { | ||||
cachedImage = { Image::ARGB, box.getWidth(), box.getHeight(), true }; | cachedImage = { Image::ARGB, box.getWidth(), box.getHeight(), true }; | ||||
Graphics g2 (cachedImage); | Graphics g2 (cachedImage); | ||||
DropShadow (Colours::black.withAlpha (0.7f), 8, { 0, 2 }).drawForPath (g2, path); | |||||
} | } | ||||
g.setColour (Colours::black); | g.setColour (Colours::black); | ||||
@@ -30,9 +30,6 @@ BubbleComponent::BubbleComponent() | |||||
: allowablePlacements (above | below | left | right) | : allowablePlacements (above | below | left | right) | ||||
{ | { | ||||
setInterceptsMouseClicks (false, false); | setInterceptsMouseClicks (false, false); | ||||
shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.35f), 5, Point<int>())); | |||||
setComponentEffect (&shadow); | |||||
} | } | ||||
BubbleComponent::~BubbleComponent() {} | BubbleComponent::~BubbleComponent() {} | ||||
@@ -178,7 +178,6 @@ private: | |||||
Rectangle<int> content; | Rectangle<int> content; | ||||
Point<int> arrowTip; | Point<int> arrowTip; | ||||
int allowablePlacements; | int allowablePlacements; | ||||
DropShadowEffect shadow; | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BubbleComponent) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BubbleComponent) | ||||
}; | }; | ||||
@@ -258,10 +258,10 @@ bool FileChooser::isPlatformDialogAvailable() | |||||
#endif | #endif | ||||
} | } | ||||
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags, FilePreviewComponent*) | |||||
std::shared_ptr<FileChooser::Pimpl> FileChooser::showPlatformDialog (FileChooser& owner, int flags, FilePreviewComponent*) | |||||
{ | { | ||||
#if JUCE_MODAL_LOOPS_PERMITTED | #if JUCE_MODAL_LOOPS_PERMITTED | ||||
return new Native (owner, flags); | |||||
return std::make_shared<Native> (owner, flags); | |||||
#else | #else | ||||
return nullptr; | return nullptr; | ||||
#endif | #endif | ||||
@@ -85,8 +85,8 @@ public: | |||||
updateScaleFactorFromNewBounds (bounds, false); | 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<Component> deletionChecker (&component); | WeakReference<Component> deletionChecker (&component); | ||||
@@ -103,13 +103,16 @@ public: | |||||
Point<int> getScreenPosition (bool physical) const | Point<int> 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) | if (physical) | ||||
return Desktop::getInstance().getDisplays().logicalToPhysical (screenBounds.getTopLeft()); | |||||
return parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (screenBounds.getTopLeft()) | |||||
: screenBounds.getTopLeft() * currentScaleFactor; | |||||
return screenBounds.getTopLeft(); | return screenBounds.getTopLeft(); | ||||
} | } | ||||
@@ -314,8 +317,8 @@ public: | |||||
updateScaleFactorFromNewBounds (physicalBounds, true); | 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<int>& newBounds, bool isPhysical) | void updateScaleFactorFromNewBounds (const Rectangle<int>& newBounds, bool isPhysical) | ||||
{ | { | ||||
if (! JUCEApplicationBase::isStandaloneApp()) | |||||
return; | |||||
Point<int> translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point<int>()); | Point<int> translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point<int>()); | ||||
const auto& desktop = Desktop::getInstance(); | const auto& desktop = Desktop::getInstance(); | ||||
@@ -377,10 +377,10 @@ private: | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Native) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Native) | ||||
}; | }; | ||||
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags, | |||||
FilePreviewComponent* preview) | |||||
std::shared_ptr<FileChooser::Pimpl> FileChooser::showPlatformDialog (FileChooser& owner, int flags, | |||||
FilePreviewComponent* preview) | |||||
{ | { | ||||
return new FileChooser::Native (owner, flags, preview); | |||||
return std::make_shared<FileChooser::Native> (owner, flags, preview); | |||||
} | } | ||||
bool FileChooser::isPlatformDialogAvailable() | bool FileChooser::isPlatformDialogAvailable() | ||||
@@ -26,32 +26,23 @@ | |||||
namespace juce | 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<Win32NativeFileChooser>, | |||||
private Thread | private Thread | ||||
{ | { | ||||
public: | public: | ||||
using Ptr = ReferenceCountedObjectPtr<Win32NativeFileChooser>; | |||||
enum { charsAvailableForResult = 32768 }; | enum { charsAvailableForResult = 32768 }; | ||||
Win32NativeFileChooser (Component* parent, int flags, FilePreviewComponent* previewComp, | Win32NativeFileChooser (Component* parent, int flags, FilePreviewComponent* previewComp, | ||||
const File& startingFile, const String& titleToUse, | const File& startingFile, const String& titleToUse, | ||||
const String& filtersToUse) | const String& filtersToUse) | ||||
: Thread ("Native Win32 FileChooser"), | : Thread ("Native Win32 FileChooser"), | ||||
owner (parent), title (titleToUse), filtersString (filtersToUse.replaceCharacter (',', ';')), | |||||
owner (parent), | |||||
title (titleToUse), | |||||
filtersString (filtersToUse.replaceCharacter (',', ';')), | |||||
selectsDirectories ((flags & FileBrowserComponent::canSelectDirectories) != 0), | selectsDirectories ((flags & FileBrowserComponent::canSelectDirectories) != 0), | ||||
isSave ((flags & FileBrowserComponent::saveMode) != 0), | isSave ((flags & FileBrowserComponent::saveMode) != 0), | ||||
warnAboutOverwrite ((flags & FileBrowserComponent::warnAboutOverwriting) != 0), | warnAboutOverwrite ((flags & FileBrowserComponent::warnAboutOverwriting) != 0), | ||||
selectMultiple ((flags & FileBrowserComponent::canSelectMultipleItems) != 0), | |||||
nativeDialogRef (nullptr), shouldCancel (0) | |||||
selectMultiple ((flags & FileBrowserComponent::canSelectMultipleItems) != 0) | |||||
{ | { | ||||
auto parentDirectory = startingFile.getParentDirectory(); | auto parentDirectory = startingFile.getParentDirectory(); | ||||
@@ -91,14 +82,13 @@ public: | |||||
// the thread should not be running | // the thread should not be running | ||||
nativeDialogRef.set (nullptr); | nativeDialogRef.set (nullptr); | ||||
weakThis = shared_from_this(); | |||||
if (async) | if (async) | ||||
{ | { | ||||
jassert (! isThreadRunning()); | jassert (! isThreadRunning()); | ||||
threadHasReference.reset(); | |||||
startThread(); | startThread(); | ||||
threadHasReference.wait (-1); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -112,7 +102,7 @@ public: | |||||
ScopedLock lock (deletingDialog); | ScopedLock lock (deletingDialog); | ||||
customComponent = nullptr; | customComponent = nullptr; | ||||
shouldCancel.set (1); | |||||
shouldCancel = true; | |||||
if (auto hwnd = nativeDialogRef.get()) | if (auto hwnd = nativeDialogRef.get()) | ||||
EndDialog (hwnd, 0); | EndDialog (hwnd, 0); | ||||
@@ -151,12 +141,12 @@ private: | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
Component::SafePointer<Component> owner; | |||||
const Component::SafePointer<Component> owner; | |||||
std::weak_ptr<Win32NativeFileChooser> weakThis; | |||||
String title, filtersString; | String title, filtersString; | ||||
std::unique_ptr<CustomComponentHolder> customComponent; | std::unique_ptr<CustomComponentHolder> customComponent; | ||||
String initialPath, returnedString; | String initialPath, returnedString; | ||||
WaitableEvent threadHasReference; | |||||
CriticalSection deletingDialog; | CriticalSection deletingDialog; | ||||
bool selectsDirectories, isSave, warnAboutOverwrite, selectMultiple; | bool selectsDirectories, isSave, warnAboutOverwrite, selectMultiple; | ||||
@@ -164,8 +154,8 @@ private: | |||||
HeapBlock<WCHAR> files; | HeapBlock<WCHAR> files; | ||||
HeapBlock<WCHAR> filters; | HeapBlock<WCHAR> filters; | ||||
Atomic<HWND> nativeDialogRef; | |||||
Atomic<int> shouldCancel; | |||||
Atomic<HWND> nativeDialogRef { nullptr }; | |||||
bool shouldCancel = false; | |||||
struct FreeLPWSTR | struct FreeLPWSTR | ||||
{ | { | ||||
@@ -173,7 +163,7 @@ private: | |||||
}; | }; | ||||
#if JUCE_MSVC | #if JUCE_MSVC | ||||
bool showDialog (IFileDialog& dialog, bool async) const | |||||
bool showDialog (IFileDialog& dialog, bool async) | |||||
{ | { | ||||
FILEOPENDIALOGOPTIONS flags = {}; | FILEOPENDIALOGOPTIONS flags = {}; | ||||
@@ -236,7 +226,49 @@ private: | |||||
if (! selectsDirectories && FAILED (dialog.SetFileTypes (numElementsInArray (spec), spec))) | if (! selectsDirectories && FAILED (dialog.SetFileTypes (numElementsInArray (spec), spec))) | ||||
return false; | return false; | ||||
return dialog.Show (static_cast<HWND> (async ? nullptr : owner->getWindowHandle())) == S_OK; | |||||
struct Events : public ComBaseClassHelper<IFileDialogEvents> | |||||
{ | |||||
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<HWND> (owner->getWindowHandle())) == S_OK; | |||||
ScopedLock lock (deletingDialog); | |||||
nativeDialogRef = nullptr; | |||||
return result; | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
@@ -451,33 +483,21 @@ private: | |||||
void run() override | 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<URL> 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<URL> 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<HWND, Win32NativeFileChooser*>& getNativeDialogList() | static HashMap<HWND, Win32NativeFileChooser*>& getNativeDialogList() | ||||
@@ -486,9 +506,9 @@ private: | |||||
return dialogs; | 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); | ScopedLock lock (deletingDialog); | ||||
getNativeDialogList().set (hdlg, this); | getNativeDialogList().set (hdlg, this); | ||||
if (shouldCancel.get() != 0) | |||||
if (shouldCancel) | |||||
{ | { | ||||
EndDialog (hdlg, 0); | EndDialog (hdlg, 0); | ||||
} | } | ||||
@@ -621,7 +641,7 @@ private: | |||||
{ | { | ||||
ScopedLock lock (deletingDialog); | ScopedLock lock (deletingDialog); | ||||
if (customComponent != nullptr && shouldCancel.get() == 0) | |||||
if (customComponent != nullptr && ! shouldCancel) | |||||
{ | { | ||||
if (FilePreviewComponent* comp = dynamic_cast<FilePreviewComponent*> (customComponent->getChildComponent (0))) | if (FilePreviewComponent* comp = dynamic_cast<FilePreviewComponent*> (customComponent->getChildComponent (0))) | ||||
{ | { | ||||
@@ -719,14 +739,15 @@ private: | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32NativeFileChooser) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32NativeFileChooser) | ||||
}; | }; | ||||
class FileChooser::Native : public Component, | |||||
class FileChooser::Native : public std::enable_shared_from_this<Native>, | |||||
public Component, | |||||
public FileChooser::Pimpl | public FileChooser::Pimpl | ||||
{ | { | ||||
public: | public: | ||||
Native (FileChooser& fileChooser, int flags, FilePreviewComponent* previewComp) | Native (FileChooser& fileChooser, int flags, FilePreviewComponent* previewComp) | ||||
: owner (fileChooser), | : owner (fileChooser), | ||||
nativeFileChooser (new Win32NativeFileChooser (this, flags, previewComp, fileChooser.startingFile, | |||||
fileChooser.title, fileChooser.filters)) | |||||
nativeFileChooser (std::make_shared<Win32NativeFileChooser> (this, flags, previewComp, fileChooser.startingFile, | |||||
fileChooser.title, fileChooser.filters)) | |||||
{ | { | ||||
auto mainMon = Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea; | auto mainMon = Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea; | ||||
@@ -743,19 +764,17 @@ public: | |||||
{ | { | ||||
exitModalState (0); | exitModalState (0); | ||||
nativeFileChooser->cancel(); | nativeFileChooser->cancel(); | ||||
nativeFileChooser = nullptr; | |||||
} | } | ||||
void launch() override | void launch() override | ||||
{ | { | ||||
SafePointer<Native> safeThis (this); | |||||
std::weak_ptr<Native> 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); | nativeFileChooser->open (true); | ||||
} | } | ||||
@@ -787,7 +806,7 @@ public: | |||||
private: | private: | ||||
FileChooser& owner; | FileChooser& owner; | ||||
Win32NativeFileChooser::Ptr nativeFileChooser; | |||||
std::shared_ptr<Win32NativeFileChooser> nativeFileChooser; | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
@@ -800,10 +819,10 @@ bool FileChooser::isPlatformDialogAvailable() | |||||
#endif | #endif | ||||
} | } | ||||
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags, | |||||
FilePreviewComponent* preview) | |||||
std::shared_ptr<FileChooser::Pimpl> FileChooser::showPlatformDialog (FileChooser& owner, int flags, | |||||
FilePreviewComponent* preview) | |||||
{ | { | ||||
return new FileChooser::Native (owner, flags, preview); | |||||
return std::make_shared<FileChooser::Native> (owner, flags, preview); | |||||
} | } | ||||
} // namespace juce | } // namespace juce |
@@ -27,7 +27,7 @@ | |||||
#include <juce_audio_plugin_client/AAX/juce_AAX_Modifier_Injector.h> | #include <juce_audio_plugin_client/AAX/juce_AAX_Modifier_Injector.h> | ||||
#endif | #endif | ||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE && JUCE_MODULE_AVAILABLE_juce_gui_extra | |||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra | |||||
#include <juce_gui_extra/embedding/juce_ScopedDPIAwarenessDisabler.h> | #include <juce_gui_extra/embedding/juce_ScopedDPIAwarenessDisabler.h> | ||||
#endif | #endif | ||||
@@ -63,6 +63,31 @@ static bool shouldDeactivateTitleBar = true; | |||||
void* getUser32Function (const char*); | 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 | #ifndef WM_TOUCH | ||||
enum | enum | ||||
@@ -408,7 +433,9 @@ static void setDPIAwareness() | |||||
static bool isPerMonitorDPIAwareProcess() | static bool isPerMonitorDPIAwareProcess() | ||||
{ | { | ||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
#if ! JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
return false; | |||||
#else | |||||
static bool dpiAware = []() -> bool | static bool dpiAware = []() -> bool | ||||
{ | { | ||||
setDPIAwareness(); | setDPIAwareness(); | ||||
@@ -423,39 +450,43 @@ static bool isPerMonitorDPIAwareProcess() | |||||
}(); | }(); | ||||
return dpiAware; | return dpiAware; | ||||
#else | |||||
return false; | |||||
#endif | #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(); | 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(); | return isPerMonitorDPIAwareProcess(); | ||||
#else | |||||
ignoreUnused (h); | |||||
return false; | |||||
#endif | #endif | ||||
} | } | ||||
static bool isPerMonitorDPIAwareThread() | static bool isPerMonitorDPIAwareThread() | ||||
{ | { | ||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
#if ! JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
return false; | |||||
#else | |||||
setDPIAwareness(); | 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(); | return isPerMonitorDPIAwareProcess(); | ||||
#else | |||||
return false; | |||||
#endif | #endif | ||||
} | } | ||||
@@ -463,27 +494,114 @@ static double getGlobalDPI() | |||||
{ | { | ||||
setDPIAwareness(); | 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<NativeImpl> ((HWND) nativeWindow); | |||||
} | |||||
ScopedThreadDPIAwarenessSetter::~ScopedThreadDPIAwarenessSetter() | |||||
{ | |||||
} | |||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra | |||||
ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() | ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() | ||||
{ | { | ||||
if (! isPerMonitorDPIAwareThread()) | if (! isPerMonitorDPIAwareThread()) | ||||
return; | return; | ||||
if (setThreadDPIAwarenessContext != nullptr) | if (setThreadDPIAwarenessContext != nullptr) | ||||
{ | |||||
previousContext = setThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE); | previousContext = setThreadDPIAwarenessContext (DPI_AWARENESS_CONTEXT_UNAWARE); | ||||
#if JUCE_DEBUG | |||||
++numActiveScopedDpiAwarenessDisablers; | |||||
#endif | |||||
} | |||||
} | } | ||||
ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() | ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() | ||||
{ | { | ||||
if (previousContext != nullptr) | if (previousContext != nullptr) | ||||
{ | |||||
setThreadDPIAwarenessContext ((DPI_AWARENESS_CONTEXT) previousContext); | setThreadDPIAwarenessContext ((DPI_AWARENESS_CONTEXT) previousContext); | ||||
#if JUCE_DEBUG | |||||
--numActiveScopedDpiAwarenessDisablers; | |||||
#endif | |||||
} | |||||
} | } | ||||
#endif | #endif | ||||
@@ -527,6 +645,14 @@ static Point<int> convertPhysicalScreenPointToLogical (Point<int> p, HWND h) noe | |||||
return p; | return p; | ||||
} | } | ||||
static Point<int> convertLogicalScreenPointToPhysical (Point<int> p, HWND h) noexcept | |||||
{ | |||||
if (isPerMonitorDPIAwareWindow (h)) | |||||
return Desktop::getInstance().getDisplays().logicalToPhysical (p, getCurrentDisplayFromScaleFactor (h)); | |||||
return p; | |||||
} | |||||
JUCE_API double getScaleFactorForWindow (HWND h) | JUCE_API double getScaleFactorForWindow (HWND h) | ||||
{ | { | ||||
// NB. Using a local function here because we need to call this method from the plug-in wrappers | // 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; | 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<int> bounds, UINT flags, bool adjustTopLeft = false) | static void setWindowPos (HWND hwnd, Rectangle<int> bounds, UINT flags, bool adjustTopLeft = false) | ||||
{ | { | ||||
ScopedThreadDPIAwarenessSetter setter { hwnd }; | |||||
if (isPerMonitorDPIAwareWindow (hwnd)) | if (isPerMonitorDPIAwareWindow (hwnd)) | ||||
{ | { | ||||
if (adjustTopLeft) | if (adjustTopLeft) | ||||
@@ -607,9 +694,7 @@ static void setWindowPos (HWND hwnd, Rectangle<int> bounds, UINT flags, bool adj | |||||
static RECT getWindowScreenRect (HWND hwnd) | static RECT getWindowScreenRect (HWND hwnd) | ||||
{ | { | ||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
setThreadDPIAwarenessForWindow (hwnd); | |||||
#endif | |||||
ScopedThreadDPIAwarenessSetter setter { hwnd }; | |||||
RECT rect; | RECT rect; | ||||
GetWindowRect (hwnd, &rect); | GetWindowRect (hwnd, &rect); | ||||
@@ -621,7 +706,10 @@ static RECT getWindowClientRect (HWND hwnd) | |||||
auto rect = getWindowScreenRect (hwnd); | auto rect = getWindowScreenRect (hwnd); | ||||
if (auto parentH = GetParent (hwnd)) | if (auto parentH = GetParent (hwnd)) | ||||
{ | |||||
ScopedThreadDPIAwarenessSetter setter { hwnd }; | |||||
MapWindowPoints (HWND_DESKTOP, parentH, (LPPOINT) &rect, 2); | MapWindowPoints (HWND_DESKTOP, parentH, (LPPOINT) &rect, 2); | ||||
} | |||||
return rect; | return rect; | ||||
} | } | ||||
@@ -634,14 +722,8 @@ static void setWindowZOrder (HWND hwnd, HWND insertAfter) | |||||
//============================================================================== | //============================================================================== | ||||
double Desktop::getDefaultMasterScale() | double Desktop::getDefaultMasterScale() | ||||
{ | { | ||||
if (! JUCEApplicationBase::isStandaloneApp() | |||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
|| isPerMonitorDPIAwareProcess() | |||||
#endif | |||||
) | |||||
{ | |||||
if (! JUCEApplicationBase::isStandaloneApp() || isPerMonitorDPIAwareProcess()) | |||||
return 1.0; | return 1.0; | ||||
} | |||||
return getGlobalDPI() / USER_DEFAULT_SCREEN_DPI; | return getGlobalDPI() / USER_DEFAULT_SCREEN_DPI; | ||||
} | } | ||||
@@ -782,9 +864,10 @@ public: | |||||
bitmapInfo.bV4V4Compression = BI_RGB; | 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); | SetMapMode (hdc, MM_TEXT); | ||||
@@ -877,10 +960,8 @@ public: | |||||
private: | private: | ||||
static bool isGraphicsCard32Bit() | 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) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsBitmapImage) | ||||
@@ -893,13 +974,13 @@ Image createSnapshotOfNativeWindow (void* nativeWindowHandle) | |||||
auto hwnd = (HWND) nativeWindowHandle; | auto hwnd = (HWND) nativeWindowHandle; | ||||
auto r = convertPhysicalScreenRectangleToLogical (rectangleFromRECT (getWindowScreenRect (hwnd)), hwnd); | 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); | auto nativeBitmap = new WindowsBitmapImage (Image::RGB, w, h, true); | ||||
Image bitmap (nativeBitmap); | Image bitmap (nativeBitmap); | ||||
HDC dc = GetDC (hwnd); | |||||
ScopedDeviceContext deviceContext { hwnd }; | |||||
if (isPerMonitorDPIAwareProcess()) | if (isPerMonitorDPIAwareProcess()) | ||||
{ | { | ||||
@@ -908,18 +989,16 @@ Image createSnapshotOfNativeWindow (void* nativeWindowHandle) | |||||
SetBrushOrgEx (nativeBitmap->hdc, 0, 0, NULL); | SetBrushOrgEx (nativeBitmap->hdc, 0, 0, NULL); | ||||
StretchBlt (nativeBitmap->hdc, 0, 0, w, h, | 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); | SRCCOPY); | ||||
SetStretchBltMode (nativeBitmap->hdc, prevStretchMode); | SetStretchBltMode (nativeBitmap->hdc, prevStretchMode); | ||||
} | } | ||||
else | 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); | return SoftwareImageType().convert (bitmap); | ||||
} | } | ||||
@@ -960,79 +1039,75 @@ namespace IconConverters | |||||
&& bm.bmWidth > 0 && bm.bmHeight > 0)) | && bm.bmWidth > 0 && bm.bmHeight > 0)) | ||||
return {}; | 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<bool> opacityMask (numPixels); | |||||
memset (bitmapImageData, 0, numColourComponents); | |||||
::DrawIconEx (dc, 0, 0, icon, bm.bmWidth, bm.bmHeight, 0, nullptr, DI_MASK); | |||||
HeapBlock<bool> 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); | ::DeleteDC (dc); | ||||
return result; | |||||
} | } | ||||
::ReleaseDC (nullptr, tempDC); | |||||
::DeleteDC (dc); | |||||
} | } | ||||
return {}; | return {}; | ||||
@@ -1433,10 +1508,8 @@ public: | |||||
auto localBounds = rectangleFromRECT (getWindowClientRect (hwnd)); | auto localBounds = rectangleFromRECT (getWindowClientRect (hwnd)); | ||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
if (isPerMonitorDPIAwareWindow (hwnd)) | if (isPerMonitorDPIAwareWindow (hwnd)) | ||||
return (localBounds.toDouble() / getPlatformScaleFactor()).toNearestInt(); | return (localBounds.toDouble() / getPlatformScaleFactor()).toNearestInt(); | ||||
#endif | |||||
return localBounds; | return localBounds; | ||||
}(); | }(); | ||||
@@ -1554,16 +1627,10 @@ public: | |||||
if (! r.withZeroOrigin().contains (localPos)) | if (! r.withZeroOrigin().contains (localPos)) | ||||
return false; | 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<int> getFrameSize() const override | BorderSize<int> getFrameSize() const override | ||||
@@ -1663,18 +1730,7 @@ public: | |||||
void repaint (const Rectangle<int>& area) override | void repaint (const Rectangle<int>& 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); | InvalidateRect (hwnd, &r, FALSE); | ||||
} | } | ||||
@@ -1760,7 +1816,7 @@ public: | |||||
if (peerIsDeleted) | if (peerIsDeleted) | ||||
return S_FALSE; | return S_FALSE; | ||||
peer.handleDragExit (dragInfo); | |||||
peer.handleDragDrop (dragInfo); | |||||
return S_OK; | return S_OK; | ||||
} | } | ||||
@@ -1795,19 +1851,8 @@ public: | |||||
private: | private: | ||||
Point<float> getMousePos (POINTL mousePos) const | Point<float> 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<float> (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 | struct DroppedData | ||||
@@ -1902,7 +1947,9 @@ public: | |||||
double getPlatformScaleFactor() const noexcept override | 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)) | if (! isPerMonitorDPIAwareWindow (hwnd)) | ||||
return 1.0; | return 1.0; | ||||
@@ -1916,8 +1963,6 @@ public: | |||||
} | } | ||||
return scaleFactor; | return scaleFactor; | ||||
#else | |||||
return 1.0; | |||||
#endif | #endif | ||||
} | } | ||||
@@ -2153,6 +2198,14 @@ private: | |||||
L"", type, 0, 0, 0, 0, parentToAddTo, nullptr, | L"", type, 0, 0, 0, 0, parentToAddTo, nullptr, | ||||
(HINSTANCE) Process::getCurrentModuleInstanceHandle(), 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) | if (hwnd != nullptr) | ||||
{ | { | ||||
SetWindowLongPtr (hwnd, 0, 0); | SetWindowLongPtr (hwnd, 0, 0); | ||||
@@ -2179,19 +2232,8 @@ private: | |||||
setDPIAwareness(); | setDPIAwareness(); | ||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
if (isPerMonitorDPIAwareThread()) | 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(); | setMessageFilter(); | ||||
updateBorderSize(); | updateBorderSize(); | ||||
@@ -3508,20 +3550,18 @@ private: | |||||
Point<float> getPointFromLocalLParam (LPARAM lParam) noexcept | Point<float> getPointFromLocalLParam (LPARAM lParam) noexcept | ||||
{ | { | ||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
auto p = pointFromPOINT (getPOINTFromLParam (lParam)); | |||||
if (isPerMonitorDPIAwareWindow (hwnd)) | 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 | // 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 | // physical screen position and then convert this to local logical coordinates | ||||
auto localPos = getPOINTFromLParam (lParam); | |||||
auto r = getWindowScreenRect (hwnd); | 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<float> (GET_X_LPARAM (lParam)), static_cast<float> (GET_Y_LPARAM (lParam)) }; | |||||
return p.toFloat(); | |||||
} | } | ||||
Point<float> getCurrentMousePos() noexcept | Point<float> getCurrentMousePos() noexcept | ||||
@@ -4431,10 +4471,8 @@ Point<float> MouseInputSource::getCurrentRawMousePosition() | |||||
auto p = pointFromPOINT (mousePos); | auto p = pointFromPOINT (mousePos); | ||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
if (isPerMonitorDPIAwareThread()) | if (isPerMonitorDPIAwareThread()) | ||||
p = Desktop::getInstance().getDisplays().physicalToLogical (p); | p = Desktop::getInstance().getDisplays().physicalToLogical (p); | ||||
#endif | |||||
return p.toFloat(); | return p.toFloat(); | ||||
} | } | ||||
@@ -1849,14 +1849,14 @@ Rectangle<int> XWindowSystem::getWindowBounds (::Window windowH, ::Window parent | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
parentScreenPosition = Desktop::getInstance().getDisplays().physicalToLogical (Point<int> (rootX, rootY)); | |||||
parentScreenPosition = Point<int> (rootX, rootY); | |||||
} | } | ||||
} | } | ||||
return { wx, wy, (int) ww, (int) wh }; | return { wx, wy, (int) ww, (int) wh }; | ||||
} | } | ||||
Point<int> XWindowSystem::getParentScreenPosition() const | |||||
Point<int> XWindowSystem::getPhysicalParentScreenPosition() const | |||||
{ | { | ||||
return parentScreenPosition; | return parentScreenPosition; | ||||
} | } | ||||
@@ -2474,7 +2474,7 @@ Array<Displays::Display> XWindowSystem::findDisplays (float masterScale) const | |||||
+ ((static_cast<double> (crtc->height) * 25.4 * 0.5) / static_cast<double> (output->mm_height)); | + ((static_cast<double> (crtc->height) * 25.4 * 0.5) / static_cast<double> (output->mm_height)); | ||||
auto scale = DisplayHelpers::getDisplayScale (output->name, d.dpi); | 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; | d.scale = masterScale * scale; | ||||
@@ -109,7 +109,7 @@ public: | |||||
BorderSize<int> getBorderSize (::Window) const; | BorderSize<int> getBorderSize (::Window) const; | ||||
Rectangle<int> getWindowBounds (::Window, ::Window parentWindow); | Rectangle<int> getWindowBounds (::Window, ::Window parentWindow); | ||||
Point<int> getParentScreenPosition() const; | |||||
Point<int> getPhysicalParentScreenPosition() const; | |||||
bool contains (::Window, Point<int> localPos) const; | bool contains (::Window, Point<int> localPos) const; | ||||
@@ -749,7 +749,7 @@ public: | |||||
? e.position.x - mouseDragStartPos.x | ? e.position.x - mouseDragStartPos.x | ||||
: mouseDragStartPos.y - e.position.y; | : mouseDragStartPos.y - e.position.y; | ||||
newPos = owner.valueToProportionOfLength (valueOnMouseDown) | |||||
newPos = owner.valueToProportionOfLength (valueWhenLastDragged) | |||||
+ mouseDiff * (1.0 / pixelsForFullDragExtent); | + mouseDiff * (1.0 / pixelsForFullDragExtent); | ||||
if (style == IncDecButtons) | if (style == IncDecButtons) | ||||
@@ -763,7 +763,7 @@ public: | |||||
auto mouseDiff = (e.position.x - mouseDragStartPos.x) | auto mouseDiff = (e.position.x - mouseDragStartPos.x) | ||||
+ (mouseDragStartPos.y - e.position.y); | + (mouseDragStartPos.y - e.position.y); | ||||
newPos = owner.valueToProportionOfLength (valueOnMouseDown) | |||||
newPos = owner.valueToProportionOfLength (valueWhenLastDragged) | |||||
+ mouseDiff * (1.0 / pixelsForFullDragExtent); | + mouseDiff * (1.0 / pixelsForFullDragExtent); | ||||
} | } | ||||
else | else | ||||
@@ -774,6 +774,7 @@ public: | |||||
newPos = 1.0 - newPos; | newPos = 1.0 - newPos; | ||||
} | } | ||||
mouseDragStartPos = e.position; | |||||
newPos = (isRotary() && ! rotaryParams.stopAtEnd) ? newPos - std::floor (newPos) | newPos = (isRotary() && ! rotaryParams.stopAtEnd) ? newPos - std::floor (newPos) | ||||
: jlimit (0.0, 1.0, newPos); | : jlimit (0.0, 1.0, newPos); | ||||
valueWhenLastDragged = owner.proportionOfLengthToValue (newPos); | valueWhenLastDragged = owner.proportionOfLengthToValue (newPos); | ||||
@@ -833,6 +833,11 @@ struct TextEditor::TextHolderComponent : public Component, | |||||
{ | { | ||||
owner.drawContent (g); | owner.drawContent (g); | ||||
} | } | ||||
void setTopLeftPosition(Point<int> new_position) override { | |||||
Component::setTopLeftPosition(new_position); | |||||
owner.textChanged(); | |||||
} | |||||
void restartTimer() | void restartTimer() | ||||
{ | { | ||||
@@ -1558,6 +1563,9 @@ void TextEditor::moveCaretTo (const int newPosition, const bool isSelecting) | |||||
moveCaret (newPosition); | moveCaret (newPosition); | ||||
selection = Range<int>::emptyRange (getCaretPosition()); | selection = Range<int>::emptyRange (getCaretPosition()); | ||||
} | } | ||||
if (listeners.size() != 0 || onTextChange != nullptr) | |||||
postCommandMessage (TextEditorDefs::textChangeMessageId); | |||||
} | } | ||||
int TextEditor::getTextIndexAt (const int x, const int y) | int TextEditor::getTextIndexAt (const int x, const int y) | ||||
@@ -2141,6 +2149,9 @@ void TextEditor::focusGained (FocusChangeType cause) | |||||
repaint(); | repaint(); | ||||
updateCaretPosition(); | updateCaretPosition(); | ||||
if (listeners.size() != 0 || onTextChange != nullptr) | |||||
postCommandMessage (TextEditorDefs::textChangeMessageId); | |||||
} | } | ||||
void TextEditor::focusLost (FocusChangeType) | void TextEditor::focusLost (FocusChangeType) | ||||
@@ -26,8 +26,6 @@ | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
#if (JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE) || DOXYGEN | |||||
//============================================================================== | //============================================================================== | ||||
/** | /** | ||||
A Windows-specific class that temporarily sets the DPI awareness context of | A Windows-specific class that temporarily sets the DPI awareness context of | ||||
@@ -52,6 +50,5 @@ public: | |||||
private: | private: | ||||
void* previousContext = nullptr; | void* previousContext = nullptr; | ||||
}; | }; | ||||
#endif | |||||
} // namespace juce | } // namespace juce |
@@ -39,6 +39,7 @@ | |||||
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1 | #define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1 | ||||
#define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 | #define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 | ||||
#define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1 | #define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1 | ||||
#define JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER 1 | |||||
#ifndef JUCE_PUSH_NOTIFICATIONS | #ifndef JUCE_PUSH_NOTIFICATIONS | ||||
#define JUCE_PUSH_NOTIFICATIONS 0 | #define JUCE_PUSH_NOTIFICATIONS 0 | ||||
@@ -192,3 +193,9 @@ | |||||
#include "native/juce_android_WebBrowserComponent.cpp" | #include "native/juce_android_WebBrowserComponent.cpp" | ||||
#endif | #endif | ||||
#endif | #endif | ||||
//============================================================================== | |||||
#if ! JUCE_WINDOWS | |||||
juce::ScopedDPIAwarenessDisabler::ScopedDPIAwarenessDisabler() { ignoreUnused (previousContext); } | |||||
juce::ScopedDPIAwarenessDisabler::~ScopedDPIAwarenessDisabler() {} | |||||
#endif |
@@ -26,8 +26,6 @@ | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
void setThreadDPIAwarenessForWindow (HWND); | |||||
class HWNDComponent::Pimpl : public ComponentMovementWatcher | class HWNDComponent::Pimpl : public ComponentMovementWatcher | ||||
{ | { | ||||
public: | public: | ||||
@@ -52,13 +50,13 @@ public: | |||||
{ | { | ||||
auto area = (peer->getAreaCoveredBy (owner).toFloat() * peer->getPlatformScaleFactor()).getSmallestIntegerContainer(); | auto area = (peer->getAreaCoveredBy (owner).toFloat() * peer->getPlatformScaleFactor()).getSmallestIntegerContainer(); | ||||
setThreadDPIAwarenessForWindow (hwnd); | |||||
UINT flagsToSend = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER; | UINT flagsToSend = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER; | ||||
if (! wasMoved) flagsToSend |= SWP_NOMOVE; | if (! wasMoved) flagsToSend |= SWP_NOMOVE; | ||||
if (! wasResized) flagsToSend |= SWP_NOSIZE; | if (! wasResized) flagsToSend |= SWP_NOSIZE; | ||||
ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { hwnd }; | |||||
SetWindowPos (hwnd, nullptr, area.getX(), area.getY(), area.getWidth(), area.getHeight(), flagsToSend); | SetWindowPos (hwnd, nullptr, area.getX(), area.getY(), area.getWidth(), area.getHeight(), flagsToSend); | ||||
} | } | ||||
} | } | ||||
@@ -101,7 +99,7 @@ public: | |||||
{ | { | ||||
if (auto* peer = owner.getPeer()) | if (auto* peer = owner.getPeer()) | ||||
{ | { | ||||
setThreadDPIAwarenessForWindow (hwnd); | |||||
ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { hwnd }; | |||||
RECT r; | RECT r; | ||||
GetWindowRect (hwnd, &r); | GetWindowRect (hwnd, &r); | ||||
@@ -37,6 +37,7 @@ | |||||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | #define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1 | ||||
#define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 | #define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1 | ||||
#define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1 | #define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1 | ||||
#define JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER 1 | |||||
#include "juce_opengl.h" | #include "juce_opengl.h" | ||||
@@ -124,7 +124,7 @@ | |||||
It's mandatory in OpenGL 3.0 to specify the GLSL version. | It's mandatory in OpenGL 3.0 to specify the GLSL version. | ||||
*/ | */ | ||||
#if JUCE_OPENGL3 | #if JUCE_OPENGL3 | ||||
#if JUCE_OPENGL_ES | |||||
#if JUCE_OPENGL_ES || OPENGL_ES | |||||
#define JUCE_GLSL_VERSION "#version 300 es" | #define JUCE_GLSL_VERSION "#version 300 es" | ||||
#else | #else | ||||
#define JUCE_GLSL_VERSION "#version 150" | #define JUCE_GLSL_VERSION "#version 150" | ||||
@@ -126,6 +126,14 @@ enum MissingOpenGLDefinitions | |||||
GL_DYNAMIC_DRAW = 0x88E8, | GL_DYNAMIC_DRAW = 0x88E8, | ||||
GL_STREAM_DRAW = 0x88E0, | 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_NUMBER_PIXEL_FORMATS_ARB = 0x2000, | ||||
WGL_DRAW_TO_WINDOW_ARB = 0x2001, | WGL_DRAW_TO_WINDOW_ARB = 0x2001, | ||||
WGL_ACCELERATION_ARB = 0x2003, | WGL_ACCELERATION_ARB = 0x2003, | ||||
@@ -69,8 +69,8 @@ public: | |||||
GLint attribs[] = | GLint attribs[] = | ||||
{ | { | ||||
GLX_RGBA, | |||||
GLX_DOUBLEBUFFER, | |||||
GLX_RENDER_TYPE, GLX_RGBA_BIT, | |||||
GLX_DOUBLEBUFFER, True, | |||||
GLX_RED_SIZE, cPixelFormat.redBits, | GLX_RED_SIZE, cPixelFormat.redBits, | ||||
GLX_GREEN_SIZE, cPixelFormat.greenBits, | GLX_GREEN_SIZE, cPixelFormat.greenBits, | ||||
GLX_BLUE_SIZE, cPixelFormat.blueBits, | GLX_BLUE_SIZE, cPixelFormat.blueBits, | ||||
@@ -81,13 +81,21 @@ public: | |||||
GLX_ACCUM_GREEN_SIZE, cPixelFormat.accumulationBufferGreenBits, | GLX_ACCUM_GREEN_SIZE, cPixelFormat.accumulationBufferGreenBits, | ||||
GLX_ACCUM_BLUE_SIZE, cPixelFormat.accumulationBufferBlueBits, | GLX_ACCUM_BLUE_SIZE, cPixelFormat.accumulationBufferBlueBits, | ||||
GLX_ACCUM_ALPHA_SIZE, cPixelFormat.accumulationBufferAlphaBits, | GLX_ACCUM_ALPHA_SIZE, cPixelFormat.accumulationBufferAlphaBits, | ||||
GLX_X_RENDERABLE, True, | |||||
None | 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; | return; | ||||
bestVisual = glXGetVisualFromFBConfig (display, *fbConfig); | |||||
if (bestVisual == nullptr) { | |||||
X11Symbols::getInstance()->xFree (fbConfig); | |||||
return; | |||||
} | |||||
auto* peer = component.getPeer(); | auto* peer = component.getPeer(); | ||||
jassert (peer != nullptr); | jassert (peer != nullptr); | ||||
@@ -139,6 +147,9 @@ public: | |||||
} | } | ||||
} | } | ||||
if (fbConfig != nullptr) | |||||
X11Symbols::getInstance()->xFree (fbConfig); | |||||
if (bestVisual != nullptr) | if (bestVisual != nullptr) | ||||
X11Symbols::getInstance()->xFree (bestVisual); | X11Symbols::getInstance()->xFree (bestVisual); | ||||
} | } | ||||
@@ -146,7 +157,18 @@ public: | |||||
bool initialiseOnRenderThread (OpenGLContext& c) | bool initialiseOnRenderThread (OpenGLContext& c) | ||||
{ | { | ||||
XWindowSystemUtilities::ScopedXLock xLock; | 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(); | c.makeActive(); | ||||
context = &c; | context = &c; | ||||
@@ -240,6 +262,7 @@ private: | |||||
int swapFrames = 1; | int swapFrames = 1; | ||||
Rectangle<int> bounds; | Rectangle<int> bounds; | ||||
XVisualInfo* bestVisual = nullptr; | XVisualInfo* bestVisual = nullptr; | ||||
GLXFBConfig* fbConfig = nullptr; | |||||
void* contextToShareWith; | void* contextToShareWith; | ||||
OpenGLContext* context = nullptr; | OpenGLContext* context = nullptr; | ||||
@@ -28,10 +28,6 @@ namespace juce | |||||
extern ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component&, void* parent); | extern ComponentPeer* createNonRepaintingEmbeddedWindowsPeer (Component&, void* parent); | ||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
extern void setThreadDPIAwarenessForWindow (HWND); | |||||
#endif | |||||
//============================================================================== | //============================================================================== | ||||
class OpenGLContext::NativeContext | class OpenGLContext::NativeContext | ||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE | #if JUCE_WIN_PER_MONITOR_DPI_AWARE | ||||
@@ -97,15 +93,17 @@ public: | |||||
bool initialiseOnRenderThread (OpenGLContext& c) | bool initialiseOnRenderThread (OpenGLContext& c) | ||||
{ | { | ||||
#if JUCE_WIN_PER_MONITOR_DPI_AWARE | |||||
setThreadDPIAwarenessForWindow ((HWND) nativeWindow->getNativeHandle()); | |||||
#endif | |||||
threadAwarenessSetter = std::make_unique<ScopedThreadDPIAwarenessSetter> (nativeWindow->getNativeHandle()); | |||||
context = &c; | context = &c; | ||||
return true; | return true; | ||||
} | } | ||||
void shutdownOnRenderThread() { deactivateCurrentContext(); context = nullptr; } | |||||
void shutdownOnRenderThread() | |||||
{ | |||||
deactivateCurrentContext(); | |||||
context = nullptr; | |||||
threadAwarenessSetter = nullptr; | |||||
} | |||||
static void deactivateCurrentContext() { wglMakeCurrent (nullptr, nullptr); } | static void deactivateCurrentContext() { wglMakeCurrent (nullptr, nullptr); } | ||||
bool makeActive() const noexcept { return isActive() || wglMakeCurrent (dc, renderContext) != FALSE; } | bool makeActive() const noexcept { return isActive() || wglMakeCurrent (dc, renderContext) != FALSE; } | ||||
@@ -170,6 +168,7 @@ private: | |||||
std::unique_ptr<DummyComponent> dummyComponent; | std::unique_ptr<DummyComponent> dummyComponent; | ||||
std::unique_ptr<ComponentPeer> nativeWindow; | std::unique_ptr<ComponentPeer> nativeWindow; | ||||
std::unique_ptr<ScopedThreadDPIAwarenessSetter> threadAwarenessSetter; | |||||
HGLRC renderContext; | HGLRC renderContext; | ||||
HDC dc; | HDC dc; | ||||
OpenGLContext* context = {}; | OpenGLContext* context = {}; | ||||
@@ -219,7 +218,13 @@ private: | |||||
void createNativeWindow (Component& component) | void createNativeWindow (Component& component) | ||||
{ | { | ||||
auto* topComp = component.getTopLevelComponent(); | 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()) | if (auto* peer = topComp->getPeer()) | ||||
{ | { | ||||
@@ -285,6 +290,8 @@ private: | |||||
atts[n++] = WGL_DRAW_TO_WINDOW_ARB; atts[n++] = GL_TRUE; | atts[n++] = WGL_DRAW_TO_WINDOW_ARB; atts[n++] = GL_TRUE; | ||||
atts[n++] = WGL_SUPPORT_OPENGL_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_DOUBLE_BUFFER_ARB; atts[n++] = GL_TRUE; | ||||
atts[n++] = WGL_PIXEL_TYPE_ARB; atts[n++] = WGL_TYPE_RGBA_ARB; | atts[n++] = WGL_PIXEL_TYPE_ARB; atts[n++] = WGL_TYPE_RGBA_ARB; | ||||
atts[n++] = WGL_ACCELERATION_ARB; | atts[n++] = WGL_ACCELERATION_ARB; | ||||
@@ -83,7 +83,7 @@ void OpenGLHelpers::enableScissorTest (Rectangle<int> clip) | |||||
String OpenGLHelpers::translateVertexShaderToV3 (const String& code) | String OpenGLHelpers::translateVertexShaderToV3 (const String& code) | ||||
{ | { | ||||
#if JUCE_OPENGL3 | |||||
#if JUCE_OPENGL3 || OPENGL_ES | |||||
if (OpenGLShaderProgram::getLanguageVersion() > 1.2) | if (OpenGLShaderProgram::getLanguageVersion() > 1.2) | ||||
{ | { | ||||
String output; | String output; | ||||
@@ -119,7 +119,7 @@ String OpenGLHelpers::translateVertexShaderToV3 (const String& code) | |||||
String OpenGLHelpers::translateFragmentShaderToV3 (const String& code) | String OpenGLHelpers::translateFragmentShaderToV3 (const String& code) | ||||
{ | { | ||||
#if JUCE_OPENGL3 | |||||
#if JUCE_OPENGL3 || OPENGL_ES | |||||
if (OpenGLShaderProgram::getLanguageVersion() > 1.2) | if (OpenGLShaderProgram::getLanguageVersion() > 1.2) | ||||
return JUCE_GLSL_VERSION "\n" | return JUCE_GLSL_VERSION "\n" | ||||
"out " JUCE_MEDIUMP " vec4 fragColor;\n" | "out " JUCE_MEDIUMP " vec4 fragColor;\n" | ||||