Browse Source

Update to JUCE 6.0.4

tags/v2.3.0-RC1
falkTX 4 years ago
parent
commit
1b824e51fc
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
100 changed files with 2510 additions and 1770 deletions
  1. +29
    -4
      source/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h
  2. +24
    -24
      source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp
  3. +8
    -8
      source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h
  4. +1
    -1
      source/modules/juce_audio_basics/juce_audio_basics.h
  5. +1
    -1
      source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h
  6. +1
    -1
      source/modules/juce_audio_basics/midi/juce_MidiMessage.h
  7. +5
    -4
      source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
  8. +79
    -22
      source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp
  9. +5
    -2
      source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h
  10. +45
    -75
      source/modules/juce_audio_devices/juce_audio_devices.cpp
  11. +18
    -11
      source/modules/juce_audio_devices/juce_audio_devices.h
  12. +11
    -2
      source/modules/juce_audio_devices/midi_io/juce_MidiDevices.h
  13. +0
    -15
      source/modules/juce_audio_devices/native/juce_android_Audio.cpp
  14. +18
    -23
      source/modules/juce_audio_devices/native/juce_android_Midi.cpp
  15. +35
    -38
      source/modules/juce_audio_devices/native/juce_android_Oboe.cpp
  16. +4
    -10
      source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp
  17. +0
    -6
      source/modules/juce_audio_devices/native/juce_ios_Audio.cpp
  18. +0
    -5
      source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp
  19. +26
    -29
      source/modules/juce_audio_devices/native/juce_linux_Bela.cpp
  20. +205
    -163
      source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp
  21. +32
    -14
      source/modules/juce_audio_devices/native/juce_linux_Midi.cpp
  22. +0
    -6
      source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
  23. +26
    -17
      source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp
  24. +8
    -13
      source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp
  25. +0
    -6
      source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp
  26. +38
    -40
      source/modules/juce_audio_devices/native/juce_win32_Midi.cpp
  27. +324
    -125
      source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp
  28. +10
    -11
      source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
  29. +87
    -20
      source/modules/juce_audio_processors/format_types/juce_VST3Common.h
  30. +3
    -1
      source/modules/juce_audio_processors/format_types/juce_VST3Headers.h
  31. +169
    -155
      source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
  32. +0
    -7
      source/modules/juce_audio_processors/format_types/juce_VSTCommon.h
  33. +4
    -1
      source/modules/juce_audio_processors/format_types/juce_VSTInterface.h
  34. +9
    -10
      source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  35. +1
    -1
      source/modules/juce_audio_processors/juce_audio_processors.h
  36. +0
    -31
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
  37. +9
    -2
      source/modules/juce_audio_processors/utilities/juce_ParameterAttachments.cpp
  38. +1
    -0
      source/modules/juce_audio_processors/utilities/juce_ParameterAttachments.h
  39. +4
    -4
      source/modules/juce_core/containers/juce_ArrayBase.cpp
  40. +2
    -2
      source/modules/juce_core/containers/juce_Variant.cpp
  41. +1
    -1
      source/modules/juce_core/files/juce_File.h
  42. +9
    -0
      source/modules/juce_core/files/juce_RangedDirectoryIterator.cpp
  43. +6
    -0
      source/modules/juce_core/files/juce_RangedDirectoryIterator.h
  44. +1
    -1
      source/modules/juce_core/juce_core.h
  45. +1
    -1
      source/modules/juce_core/memory/juce_ByteOrder.h
  46. +47
    -7
      source/modules/juce_core/memory/juce_Memory.h
  47. +2
    -2
      source/modules/juce_core/native/juce_linux_SystemStats.cpp
  48. +13
    -2
      source/modules/juce_core/native/juce_mac_Files.mm
  49. +11
    -7
      source/modules/juce_core/native/juce_mac_SystemStats.mm
  50. +32
    -33
      source/modules/juce_core/native/juce_osx_ObjCHelpers.h
  51. +4
    -4
      source/modules/juce_core/native/juce_posix_IPAddress.h
  52. +11
    -0
      source/modules/juce_core/native/juce_win32_ComSmartPtr.h
  53. +0
    -3
      source/modules/juce_core/native/juce_win32_SystemStats.cpp
  54. +1
    -1
      source/modules/juce_core/native/juce_win32_Threads.cpp
  55. +3
    -1
      source/modules/juce_core/system/juce_PlatformDefs.h
  56. +1
    -1
      source/modules/juce_core/system/juce_StandardHeader.h
  57. +2
    -3
      source/modules/juce_core/system/juce_SystemStats.h
  58. +0
    -1
      source/modules/juce_core/system/juce_TargetPlatform.h
  59. +6
    -6
      source/modules/juce_core/text/juce_String.cpp
  60. +1
    -1
      source/modules/juce_core/text/juce_String.h
  61. +1
    -1
      source/modules/juce_data_structures/juce_data_structures.h
  62. +1
    -1
      source/modules/juce_events/juce_events.h
  63. +5
    -1
      source/modules/juce_events/native/juce_mac_MessageManager.mm
  64. +0
    -2
      source/modules/juce_graphics/fonts/juce_Font.h
  65. +14
    -4
      source/modules/juce_graphics/geometry/juce_Point.h
  66. +0
    -4
      source/modules/juce_graphics/juce_graphics.cpp
  67. +2
    -2
      source/modules/juce_graphics/juce_graphics.h
  68. +34
    -34
      source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm
  69. +29
    -0
      source/modules/juce_graphics/native/juce_mac_Fonts.mm
  70. +26
    -8
      source/modules/juce_gui_basics/components/juce_Component.cpp
  71. +22
    -0
      source/modules/juce_gui_basics/components/juce_Component.h
  72. +42
    -34
      source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp
  73. +1
    -1
      source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp
  74. +2
    -2
      source/modules/juce_gui_basics/juce_gui_basics.h
  75. +30
    -8
      source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp
  76. +2
    -2
      source/modules/juce_gui_basics/misc/juce_JUCESplashScreen.cpp
  77. +2
    -2
      source/modules/juce_gui_basics/misc/juce_JUCESplashScreen.h
  78. +1
    -0
      source/modules/juce_gui_basics/native/juce_ios_ContentSharer.cpp
  79. +77
    -36
      source/modules/juce_gui_basics/native/juce_ios_FileChooser.mm
  80. +134
    -261
      source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm
  81. +3
    -81
      source/modules/juce_gui_basics/native/juce_ios_Windowing.mm
  82. +11
    -10
      source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp
  83. +12
    -4
      source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp
  84. +1
    -1
      source/modules/juce_gui_basics/native/juce_mac_FileChooser.mm
  85. +0
    -23
      source/modules/juce_gui_basics/native/juce_mac_MainMenu.mm
  86. +21
    -17
      source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm
  87. +1
    -1
      source/modules/juce_gui_basics/native/juce_mac_Windowing.mm
  88. +182
    -20
      source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp
  89. +1
    -4
      source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp
  90. +1
    -1
      source/modules/juce_gui_basics/native/x11/juce_linux_X11_DragAndDrop.cpp
  91. +2
    -0
      source/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.cpp
  92. +8
    -0
      source/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.h
  93. +193
    -95
      source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp
  94. +29
    -5
      source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h
  95. +1
    -1
      source/modules/juce_gui_basics/properties/juce_MultiChoicePropertyComponent.cpp
  96. +172
    -89
      source/modules/juce_gui_basics/widgets/juce_TextEditor.cpp
  97. +24
    -7
      source/modules/juce_gui_basics/widgets/juce_TextEditor.h
  98. +14
    -14
      source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp
  99. +5
    -9
      source/modules/juce_gui_basics/windows/juce_CallOutBox.h
  100. +10
    -0
      source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp

+ 29
- 4
source/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h View File

@@ -122,10 +122,35 @@ public:
bool isLooping;
//==============================================================================
bool operator== (const CurrentPositionInfo& other) const noexcept;
bool operator!= (const CurrentPositionInfo& other) const noexcept;
void resetToDefault();
bool operator== (const CurrentPositionInfo& other) const noexcept
{
return timeInSamples == other.timeInSamples
&& ppqPosition == other.ppqPosition
&& editOriginTime == other.editOriginTime
&& ppqPositionOfLastBarStart == other.ppqPositionOfLastBarStart
&& frameRate == other.frameRate
&& isPlaying == other.isPlaying
&& isRecording == other.isRecording
&& bpm == other.bpm
&& timeSigNumerator == other.timeSigNumerator
&& timeSigDenominator == other.timeSigDenominator
&& ppqLoopStart == other.ppqLoopStart
&& ppqLoopEnd == other.ppqLoopEnd
&& isLooping == other.isLooping;
}
bool operator!= (const CurrentPositionInfo& other) const noexcept
{
return ! operator== (other);
}
void resetToDefault()
{
zerostruct (*this);
timeSigNumerator = 4;
timeSigDenominator = 4;
bpm = 120;
}
};
//==============================================================================


+ 24
- 24
source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp View File

@@ -32,7 +32,7 @@ void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest
{
for (int i = 0; i < numSamples; ++i)
{
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
*unalignedPointerCast<uint16*> (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
intData += destBytesPerSample;
}
}
@@ -43,7 +43,7 @@ void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest
for (int i = numSamples; --i >= 0;)
{
intData -= destBytesPerSample;
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
*unalignedPointerCast<uint16*> (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
}
}
}
@@ -57,7 +57,7 @@ void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest
{
for (int i = 0; i < numSamples; ++i)
{
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
*unalignedPointerCast<uint16*> (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
intData += destBytesPerSample;
}
}
@@ -68,7 +68,7 @@ void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest
for (int i = numSamples; --i >= 0;)
{
intData -= destBytesPerSample;
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
*unalignedPointerCast<uint16*> (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
}
}
}
@@ -132,7 +132,7 @@ void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest
{
for (int i = 0; i < numSamples; ++i)
{
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
*unalignedPointerCast<uint32*> (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
intData += destBytesPerSample;
}
}
@@ -143,7 +143,7 @@ void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest
for (int i = numSamples; --i >= 0;)
{
intData -= destBytesPerSample;
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
*unalignedPointerCast<uint32*> (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
}
}
}
@@ -157,7 +157,7 @@ void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest
{
for (int i = 0; i < numSamples; ++i)
{
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
*unalignedPointerCast<uint32*> (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
intData += destBytesPerSample;
}
}
@@ -168,7 +168,7 @@ void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest
for (int i = numSamples; --i >= 0;)
{
intData -= destBytesPerSample;
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
*unalignedPointerCast<uint32*> (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])));
}
}
}
@@ -181,10 +181,10 @@ void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* de
for (int i = 0; i < numSamples; ++i)
{
*reinterpret_cast<float*> (d) = source[i];
*unalignedPointerCast<float*> (d) = source[i];
#if JUCE_BIG_ENDIAN
*reinterpret_cast<uint32*> (d) = ByteOrder::swap (*reinterpret_cast<uint32*> (d));
*unalignedPointerCast<uint32*> (d) = ByteOrder::swap (*unalignedPointerCast<uint32*> (d));
#endif
d += destBytesPerSample;
@@ -199,10 +199,10 @@ void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* de
for (int i = 0; i < numSamples; ++i)
{
*reinterpret_cast<float*> (d) = source[i];
*unalignedPointerCast<float*> (d) = source[i];
#if JUCE_LITTLE_ENDIAN
*reinterpret_cast<uint32*> (d) = ByteOrder::swap (*reinterpret_cast<uint32*> (d));
*unalignedPointerCast<uint32*> (d) = ByteOrder::swap (*unalignedPointerCast<uint32*> (d));
#endif
d += destBytesPerSample;
@@ -219,7 +219,7 @@ void AudioDataConverters::convertInt16LEToFloat (const void* source, float* dest
{
for (int i = 0; i < numSamples; ++i)
{
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint16*> (intData));
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*unalignedPointerCast<const uint16*> (intData));
intData += srcBytesPerSample;
}
}
@@ -230,7 +230,7 @@ void AudioDataConverters::convertInt16LEToFloat (const void* source, float* dest
for (int i = numSamples; --i >= 0;)
{
intData -= srcBytesPerSample;
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint16*> (intData));
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*unalignedPointerCast<const uint16*> (intData));
}
}
}
@@ -244,7 +244,7 @@ void AudioDataConverters::convertInt16BEToFloat (const void* source, float* dest
{
for (int i = 0; i < numSamples; ++i)
{
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint16*> (intData));
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*unalignedPointerCast<const uint16*> (intData));
intData += srcBytesPerSample;
}
}
@@ -255,7 +255,7 @@ void AudioDataConverters::convertInt16BEToFloat (const void* source, float* dest
for (int i = numSamples; --i >= 0;)
{
intData -= srcBytesPerSample;
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint16*> (intData));
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*unalignedPointerCast<const uint16*> (intData));
}
}
}
@@ -319,7 +319,7 @@ void AudioDataConverters::convertInt32LEToFloat (const void* source, float* dest
{
for (int i = 0; i < numSamples; ++i)
{
dest[i] = scale * (float) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint32*> (intData));
dest[i] = scale * (float) ByteOrder::swapIfBigEndian (*unalignedPointerCast<const uint32*> (intData));
intData += srcBytesPerSample;
}
}
@@ -330,7 +330,7 @@ void AudioDataConverters::convertInt32LEToFloat (const void* source, float* dest
for (int i = numSamples; --i >= 0;)
{
intData -= srcBytesPerSample;
dest[i] = scale * (float) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint32*> (intData));
dest[i] = scale * (float) ByteOrder::swapIfBigEndian (*unalignedPointerCast<const uint32*> (intData));
}
}
}
@@ -344,7 +344,7 @@ void AudioDataConverters::convertInt32BEToFloat (const void* source, float* dest
{
for (int i = 0; i < numSamples; ++i)
{
dest[i] = scale * (float) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint32*> (intData));
dest[i] = scale * (float) ByteOrder::swapIfLittleEndian (*unalignedPointerCast<const uint32*> (intData));
intData += srcBytesPerSample;
}
}
@@ -355,7 +355,7 @@ void AudioDataConverters::convertInt32BEToFloat (const void* source, float* dest
for (int i = numSamples; --i >= 0;)
{
intData -= srcBytesPerSample;
dest[i] = scale * (float) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint32*> (intData));
dest[i] = scale * (float) ByteOrder::swapIfLittleEndian (*unalignedPointerCast<const uint32*> (intData));
}
}
}
@@ -366,10 +366,10 @@ void AudioDataConverters::convertFloat32LEToFloat (const void* source, float* de
for (int i = 0; i < numSamples; ++i)
{
dest[i] = *reinterpret_cast<const float*> (s);
dest[i] = *unalignedPointerCast<const float*> (s);
#if JUCE_BIG_ENDIAN
auto d = reinterpret_cast<uint32*> (dest + i);
auto d = unalignedPointerCast<uint32*> (dest + i);
*d = ByteOrder::swap (*d);
#endif
@@ -383,10 +383,10 @@ void AudioDataConverters::convertFloat32BEToFloat (const void* source, float* de
for (int i = 0; i < numSamples; ++i)
{
dest[i] = *reinterpret_cast<const float*> (s);
dest[i] = *unalignedPointerCast<const float*> (s);
#if JUCE_LITTLE_ENDIAN
auto d = reinterpret_cast<uint32*> (dest + i);
auto d = unalignedPointerCast<uint32*> (dest + i);
*d = ByteOrder::swap (*d);
#endif


+ 8
- 8
source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h View File

@@ -409,8 +409,8 @@ public:
auto numSamplesToCopy = (size_t) jmin (newNumSamples, size);
auto newChannels = reinterpret_cast<Type**> (newData.get());
auto newChan = reinterpret_cast<Type*> (newData + channelListSize);
auto newChannels = unalignedPointerCast<Type**> (newData.get());
auto newChan = unalignedPointerCast<Type*> (newData + channelListSize);
for (int j = 0; j < newNumChannels; ++j)
{
@@ -442,10 +442,10 @@ public:
{
allocatedBytes = newTotalBytes;
allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear);
channels = reinterpret_cast<Type**> (allocatedData.get());
channels = unalignedPointerCast<Type**> (allocatedData.get());
}
auto* chan = reinterpret_cast<Type*> (allocatedData + channelListSize);
auto* chan = unalignedPointerCast<Type*> (allocatedData + channelListSize);
for (int i = 0; i < newNumChannels; ++i)
{
@@ -1127,7 +1127,7 @@ private:
void allocateData()
{
#if (! JUCE_GCC) || (__GNUC__ * 100 + __GNUC_MINOR__) >= 409
#if ! JUCE_PROJUCER_LIVE_BUILD && (! JUCE_GCC || (__GNUC__ * 100 + __GNUC_MINOR__) >= 409)
static_assert (alignof (Type) <= detail::maxAlignment,
"AudioBuffer cannot hold types with alignment requirements larger than that guaranteed by malloc");
#endif
@@ -1142,8 +1142,8 @@ private:
allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (Type) + channelListSize + 32;
allocatedData.malloc (allocatedBytes);
channels = reinterpret_cast<Type**> (allocatedData.get());
auto chan = reinterpret_cast<Type*> (allocatedData + channelListSize);
channels = unalignedPointerCast<Type**> (allocatedData.get());
auto chan = unalignedPointerCast<Type*> (allocatedData + channelListSize);
for (int i = 0; i < numChannels; ++i)
{
@@ -1167,7 +1167,7 @@ private:
else
{
allocatedData.malloc (numChannels + 1, sizeof (Type*));
channels = reinterpret_cast<Type**> (allocatedData.get());
channels = unalignedPointerCast<Type**> (allocatedData.get());
}
for (int i = 0; i < numChannels; ++i)


+ 1
- 1
source/modules/juce_audio_basics/juce_audio_basics.h View File

@@ -32,7 +32,7 @@
ID: juce_audio_basics
vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE audio and MIDI data classes
description: Classes for audio buffer manipulation, midi message handling, synthesis, etc.
website: http://www.juce.com/juce


+ 1
- 1
source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h View File

@@ -136,7 +136,7 @@ public:
//==============================================================================
/** Receives events from a MidiKeyboardState object. */
class Listener
class JUCE_API Listener
{
public:
//==============================================================================


+ 1
- 1
source/modules/juce_audio_basics/midi/juce_MidiMessage.h View File

@@ -401,7 +401,7 @@ public:
/** Returns true if the message is an aftertouch event.
For aftertouch events, use the getNoteNumber() method to find out the key
that it applies to, and getAftertouchValue() to find out the amount. Use
that it applies to, and getAfterTouchValue() to find out the amount. Use
getChannel() to find out the channel.
@see getAftertouchValue, getNoteNumber


+ 5
- 4
source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp View File

@@ -177,8 +177,9 @@ static void addIfNotNull (OwnedArray<AudioIODeviceType>& list, AudioIODeviceType
void AudioDeviceManager::createAudioDeviceTypes (OwnedArray<AudioIODeviceType>& list)
{
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (false));
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (true));
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::shared));
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::exclusive));
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::sharedLowLatency));
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound());
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO());
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio());
@@ -544,6 +545,8 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup
else if (currentAudioDevice != nullptr)
return {};
stopDevice();
if (getCurrentDeviceTypeObject() == nullptr
|| (newSetup.inputDeviceName.isEmpty() && newSetup.outputDeviceName.isEmpty()))
{
@@ -555,8 +558,6 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup
return {};
}
stopDevice();
String error;
if (currentSetup.inputDeviceName != newSetup.inputDeviceName


+ 79
- 22
source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp View File

@@ -42,48 +42,105 @@ void AudioIODeviceType::callDeviceChangeListeners()
}
//==============================================================================
#if ! JUCE_MAC
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; }
#if JUCE_MAC
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return new CoreAudioClasses::CoreAudioIODeviceType(); }
#else
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; }
#endif
#if ! JUCE_IOS
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; }
#if JUCE_IOS
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return new iOSAudioIODeviceType(); }
#else
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; }
#endif
#if ! (JUCE_WINDOWS && JUCE_WASAPI)
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; }
#if JUCE_WINDOWS && JUCE_WASAPI
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode)
{
auto windowsVersion = SystemStats::getOperatingSystemType();
if (windowsVersion < SystemStats::WinVista
|| (WasapiClasses::isLowLatencyMode (deviceMode) && windowsVersion < SystemStats::Windows10))
return nullptr;
return new WasapiClasses::WASAPIAudioIODeviceType (deviceMode);
}
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode)
{
return createAudioIODeviceType_WASAPI (exclusiveMode ? WASAPIDeviceMode::exclusive
: WASAPIDeviceMode::shared);
}
#else
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode) { return nullptr; }
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; }
#endif
#if ! (JUCE_WINDOWS && JUCE_DIRECTSOUND)
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; }
#if JUCE_WINDOWS && JUCE_DIRECTSOUND
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return new DSoundAudioIODeviceType(); }
#else
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; }
#endif
#if ! (JUCE_WINDOWS && JUCE_ASIO)
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; }
#if JUCE_WINDOWS && JUCE_ASIO
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return new ASIOAudioIODeviceType(); }
#else
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; }
#endif
#if ! (JUCE_LINUX && JUCE_ALSA)
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; }
#if JUCE_LINUX && JUCE_ALSA
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return createAudioIODeviceType_ALSA_PCMDevices(); }
#else
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; }
#endif
#if ! (JUCE_LINUX && JUCE_JACK)
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; }
#if JUCE_LINUX && JUCE_JACK
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return new JackAudioIODeviceType(); }
#else
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; }
#endif
#if ! (JUCE_LINUX && JUCE_BELA)
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return nullptr; }
#if JUCE_LINUX && JUCE_BELA
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return new BelaAudioIODeviceType(); }
#else
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return nullptr; }
#endif
#if ! JUCE_ANDROID
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; }
#if JUCE_ANDROID
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android()
{
#if JUCE_USE_ANDROID_OBOE
if (isOboeAvailable())
return nullptr;
#endif
#if JUCE_USE_ANDROID_OPENSLES
if (isOpenSLAvailable())
return nullptr;
#endif
return new AndroidAudioIODeviceType();
}
#else
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; }
#endif
#if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES)
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; }
#if JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES()
{
return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr;
}
#else
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; }
#endif
#if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OBOE)
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() { return nullptr; }
#if JUCE_ANDROID && JUCE_USE_ANDROID_OBOE
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe()
{
return isOboeAvailable() ? new OboeAudioIODeviceType() : nullptr;
}
#else
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() { return nullptr; }
#endif
} // namespace juce

+ 5
- 2
source/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h View File

@@ -149,8 +149,8 @@ public:
static AudioIODeviceType* createAudioIODeviceType_CoreAudio();
/** Creates an iOS device type if it's available on this platform, or returns null. */
static AudioIODeviceType* createAudioIODeviceType_iOSAudio();
/** Creates a WASAPI device type if it's available on this platform, or returns null. */
static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode);
/** Creates a WASAPI device type in the specified mode if it's available on this platform, or returns null. */
static AudioIODeviceType* createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode);
/** Creates a DirectSound device type if it's available on this platform, or returns null. */
static AudioIODeviceType* createAudioIODeviceType_DirectSound();
/** Creates an ASIO device type if it's available on this platform, or returns null. */
@@ -168,6 +168,9 @@ public:
/** Creates a Bela device type if it's available on this platform, or returns null. */
static AudioIODeviceType* createAudioIODeviceType_Bela();
/** This method has been deprecated. You should call the method which takes a WASAPIDeviceMode instead. */
JUCE_DEPRECATED (static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode));
protected:
explicit AudioIODeviceType (const String& typeName);


+ 45
- 75
source/modules/juce_audio_devices/juce_audio_devices.cpp View File

@@ -45,6 +45,8 @@
#include "juce_audio_devices.h"
#include "native/juce_MidiDataConcatenator.h"
//==============================================================================
#if JUCE_MAC
#define Point CarbonDummyPointName
@@ -55,6 +57,9 @@
#undef Point
#undef Component
#include "native/juce_mac_CoreAudio.cpp"
#include "native/juce_mac_CoreMidi.cpp"
#elif JUCE_IOS
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
@@ -64,13 +69,21 @@
#import <CoreMIDI/MIDINetworkSession.h>
#endif
#include "native/juce_ios_Audio.cpp"
#include "native/juce_mac_CoreMidi.cpp"
//==============================================================================
#elif JUCE_WINDOWS
#if JUCE_WASAPI
#include <mmreg.h>
#include "native/juce_win32_WASAPI.cpp"
#endif
#if JUCE_USE_WINRT_MIDI && JUCE_MSVC
#if JUCE_DIRECTSOUND
#include "native/juce_win32_DirectSound.cpp"
#endif
#if JUCE_USE_WINRT_MIDI && (JUCE_MSVC || JUCE_CLANG)
/* If you cannot find any of the header files below then you are probably
attempting to use the Windows 10 Bluetooth Low Energy API. For this to work you
need to install version 10.0.14393.0 of the Windows Standalone SDK and you may
@@ -93,6 +106,8 @@
JUCE_END_IGNORE_WARNINGS_MSVC
#endif
#include "native/juce_win32_Midi.cpp"
#if JUCE_ASIO
/* This is very frustrating - we only need to use a handful of definitions from
a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy
@@ -114,6 +129,7 @@
needed - so to simplify things, you could just copy these into your JUCE directory).
*/
#include <iasiodrv.h>
#include "native/juce_win32_ASIO.cpp"
#endif
//==============================================================================
@@ -128,6 +144,7 @@
just set the JUCE_ALSA flag to 0.
*/
#include <alsa/asoundlib.h>
#include "native/juce_linux_ALSA.cpp"
#endif
#if JUCE_JACK
@@ -140,6 +157,7 @@
JUCE with low latency audio support, just set the JUCE_JACK flag to 0.
*/
#include <jack/jack.h>
#include "native/juce_linux_JackAudio.cpp"
#endif
#if JUCE_BELA
@@ -149,89 +167,18 @@
*/
#include <Bela.h>
#include <Midi.h>
#include "native/juce_linux_Bela.cpp"
#endif
#undef SIZEOF
//==============================================================================
#elif JUCE_ANDROID
#if JUCE_USE_ANDROID_OPENSLES
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <SLES/OpenSLES_AndroidConfiguration.h>
#endif
#if JUCE_USE_ANDROID_OBOE
#if JUCE_USE_ANDROID_OPENSLES
#error "Oboe cannot be enabled at the same time as openSL! Please disable JUCE_USE_ANDROID_OPENSLES"
#endif
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter",
"-Wzero-as-null-pointer-constant",
"-Winconsistent-missing-destructor-override",
"-Wshadow-field-in-constructor",
"-Wshadow-field")
#include <oboe/Oboe.h>
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#endif
#endif
#include "audio_io/juce_AudioDeviceManager.cpp"
#include "audio_io/juce_AudioIODevice.cpp"
#include "audio_io/juce_AudioIODeviceType.cpp"
#include "midi_io/juce_MidiMessageCollector.cpp"
#include "midi_io/juce_MidiDevices.cpp"
#include "sources/juce_AudioSourcePlayer.cpp"
#include "sources/juce_AudioTransportSource.cpp"
#include "native/juce_MidiDataConcatenator.h"
//==============================================================================
#if JUCE_MAC
#include "native/juce_mac_CoreAudio.cpp"
#include "native/juce_mac_CoreMidi.cpp"
//==============================================================================
#elif JUCE_IOS
#include "native/juce_ios_Audio.cpp"
#include "native/juce_mac_CoreMidi.cpp"
//==============================================================================
#elif JUCE_WINDOWS
#if JUCE_WASAPI
#include "native/juce_win32_WASAPI.cpp"
#endif
#if JUCE_DIRECTSOUND
#include "native/juce_win32_DirectSound.cpp"
#endif
#include "native/juce_win32_Midi.cpp"
#if JUCE_ASIO
#include "native/juce_win32_ASIO.cpp"
#endif
//==============================================================================
#elif JUCE_LINUX
#if JUCE_ALSA
#include "native/juce_linux_ALSA.cpp"
#endif
#if JUCE_JACK
#include "native/juce_linux_JackAudio.cpp"
#endif
#if JUCE_BELA
#include "native/juce_linux_Bela.cpp"
#else
#if ! JUCE_BELA
#include "native/juce_linux_Midi.cpp"
#endif
//==============================================================================
#elif JUCE_ANDROID
#include "native/juce_android_Audio.cpp"
#include "native/juce_android_Midi.cpp"
@@ -239,10 +186,25 @@
#include "native/juce_android_HighPerformanceAudioHelpers.h"
#if JUCE_USE_ANDROID_OPENSLES
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <SLES/OpenSLES_AndroidConfiguration.h>
#include "native/juce_android_OpenSL.cpp"
#endif
#if JUCE_USE_ANDROID_OBOE
#if JUCE_USE_ANDROID_OPENSLES
#error "Oboe cannot be enabled at the same time as openSL! Please disable JUCE_USE_ANDROID_OPENSLES"
#endif
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter",
"-Wzero-as-null-pointer-constant",
"-Winconsistent-missing-destructor-override",
"-Wshadow-field-in-constructor",
"-Wshadow-field")
#include <oboe/Oboe.h>
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#include "native/juce_android_Oboe.cpp"
#endif
#endif
@@ -259,3 +221,11 @@ namespace juce
bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; }
}
#endif
#include "audio_io/juce_AudioDeviceManager.cpp"
#include "audio_io/juce_AudioIODevice.cpp"
#include "audio_io/juce_AudioIODeviceType.cpp"
#include "midi_io/juce_MidiMessageCollector.cpp"
#include "midi_io/juce_MidiDevices.cpp"
#include "sources/juce_AudioSourcePlayer.cpp"
#include "sources/juce_AudioTransportSource.cpp"

+ 18
- 11
source/modules/juce_audio_devices/juce_audio_devices.h View File

@@ -32,7 +32,7 @@
ID: juce_audio_devices
vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE audio and MIDI I/O device classes
description: Classes to play and record from audio and MIDI I/O devices
website: http://www.juce.com/juce
@@ -88,21 +88,12 @@
#endif
/** Config: JUCE_WASAPI
Enables WASAPI audio devices (Windows Vista and above). See also the
JUCE_WASAPI_EXCLUSIVE flag.
Enables WASAPI audio devices (Windows Vista and above).
*/
#ifndef JUCE_WASAPI
#define JUCE_WASAPI 1
#endif
/** Config: JUCE_WASAPI_EXCLUSIVE
Enables WASAPI audio devices in exclusive mode (Windows Vista and above).
*/
#ifndef JUCE_WASAPI_EXCLUSIVE
#define JUCE_WASAPI_EXCLUSIVE 0
#endif
/** Config: JUCE_DIRECTSOUND
Enables DirectSound audio (MS Windows only).
*/
@@ -174,6 +165,22 @@
//==============================================================================
#include "midi_io/juce_MidiDevices.h"
#include "midi_io/juce_MidiMessageCollector.h"
namespace juce
{
/** Available modes for the WASAPI audio device.
Pass one of these to the AudioIODeviceType::createAudioIODeviceType_WASAPI()
method to create a WASAPI AudioIODeviceType object in this mode.
*/
enum class WASAPIDeviceMode
{
shared,
exclusive,
sharedLowLatency
};
}
#include "audio_io/juce_AudioIODevice.h"
#include "audio_io/juce_AudioIODeviceType.h"
#include "audio_io/juce_SystemAudioVolume.h"


+ 11
- 2
source/modules/juce_audio_devices/midi_io/juce_MidiDevices.h View File

@@ -164,12 +164,16 @@ public:
/** Deprecated. */
static std::unique_ptr<MidiInput> openDevice (int, MidiInputCallback*);
/** @internal */
class Pimpl;
private:
//==============================================================================
explicit MidiInput (const String&, const String&);
MidiDeviceInfo deviceInfo;
void* internal = nullptr;
std::unique_ptr<Pimpl> internal;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput)
};
@@ -350,6 +354,9 @@ public:
/** Deprecated. */
static std::unique_ptr<MidiOutput> openDevice (int);
/** @internal */
class Pimpl;
private:
//==============================================================================
struct PendingMessage
@@ -368,7 +375,9 @@ private:
void run() override;
MidiDeviceInfo deviceInfo;
void* internal = nullptr;
std::unique_ptr<Pimpl> internal;
CriticalSection lock;
PendingMessage* firstMessage = nullptr;


+ 0
- 15
source/modules/juce_audio_devices/native/juce_android_Audio.cpp View File

@@ -478,19 +478,4 @@ private:
extern bool isOboeAvailable();
extern bool isOpenSLAvailable();
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android()
{
#if JUCE_USE_ANDROID_OBOE
if (isOboeAvailable())
return nullptr;
#endif
#if JUCE_USE_ANDROID_OPENSLES
if (isOpenSLAvailable())
return nullptr;
#endif
return new AndroidAudioIODeviceType();
}
} // namespace juce

+ 18
- 23
source/modules/juce_audio_devices/native/juce_android_Midi.cpp View File

@@ -350,17 +350,17 @@ DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiPort, "com/rmsl/juce/JuceMidiSupport$Juc
#undef JNI_CLASS_MEMBERS
//==============================================================================
class AndroidMidiInput
class MidiInput::Pimpl
{
public:
AndroidMidiInput (MidiInput* midiInput, int deviceID, juce::MidiInputCallback* midiInputCallback, jobject deviceManager)
Pimpl (MidiInput* midiInput, int deviceID, juce::MidiInputCallback* midiInputCallback, jobject deviceManager)
: juceMidiInput (midiInput), callback (midiInputCallback), midiConcatenator (2048),
javaMidiDevice (LocalRef<jobject>(getEnv()->CallObjectMethod (deviceManager, MidiDeviceManager.openMidiInputPortWithID,
(jint) deviceID, (jlong) this)))
{
}
~AndroidMidiInput()
~Pimpl()
{
if (jobject d = javaMidiDevice.get())
{
@@ -416,7 +416,7 @@ public:
static void handleReceive (JNIEnv*, jobject, jlong host, jbyteArray byteArray,
jint offset, jint len, jlong timestamp)
{
auto* myself = reinterpret_cast<AndroidMidiInput*> (host);
auto* myself = reinterpret_cast<Pimpl*> (host);
myself->handleMidi (byteArray, offset, len, timestamp);
}
@@ -429,15 +429,15 @@ private:
};
//==============================================================================
class AndroidMidiOutput
class MidiOutput::Pimpl
{
public:
AndroidMidiOutput (const LocalRef<jobject>& midiDevice)
Pimpl (const LocalRef<jobject>& midiDevice)
: javaMidiDevice (midiDevice)
{
}
~AndroidMidiOutput()
~Pimpl()
{
if (jobject d = javaMidiDevice.get())
{
@@ -468,7 +468,7 @@ private:
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
CALLBACK (AndroidMidiInput::handleReceive, "handleReceive", "(J[BIIJ)V" )
CALLBACK (MidiInput::Pimpl::handleReceive, "handleReceive", "(J[BIIJ)V" )
DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiInputPort, "com/rmsl/juce/JuceMidiSupport$JuceMidiInputPort", 23)
#undef JNI_CLASS_MEMBERS
@@ -509,11 +509,11 @@ public:
return {};
}
AndroidMidiInput* openMidiInputPortWithID (int deviceID, MidiInput* juceMidiInput, juce::MidiInputCallback* callback)
MidiInput::Pimpl* openMidiInputPortWithID (int deviceID, MidiInput* juceMidiInput, juce::MidiInputCallback* callback)
{
if (auto dm = deviceManager.get())
{
std::unique_ptr<AndroidMidiInput> androidMidiInput (new AndroidMidiInput (juceMidiInput, deviceID, callback, dm));
auto androidMidiInput = std::make_unique<MidiInput::Pimpl> (juceMidiInput, deviceID, callback, dm);
if (androidMidiInput->isOpen())
return androidMidiInput.release();
@@ -522,11 +522,11 @@ public:
return nullptr;
}
AndroidMidiOutput* openMidiOutputPortWithID (int deviceID)
MidiOutput::Pimpl* openMidiOutputPortWithID (int deviceID)
{
if (auto dm = deviceManager.get())
if (auto javaMidiPort = getEnv()->CallObjectMethod (dm, MidiDeviceManager.openMidiOutputPortWithID, (jint) deviceID))
return new AndroidMidiOutput (LocalRef<jobject>(javaMidiPort));
return new MidiOutput::Pimpl (LocalRef<jobject>(javaMidiPort));
return nullptr;
}
@@ -564,7 +564,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier
if (auto* port = manager.openMidiInputPortWithID (deviceIdentifier.getIntValue(), midiInput.get(), callback))
{
midiInput->internal = port;
midiInput->internal.reset (port);
midiInput->setName (port->getName());
return midiInput;
@@ -601,20 +601,17 @@ MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier)
{
}
MidiInput::~MidiInput()
{
delete reinterpret_cast<AndroidMidiInput*> (internal);
}
MidiInput::~MidiInput() = default;
void MidiInput::start()
{
if (auto* mi = reinterpret_cast<AndroidMidiInput*> (internal))
if (auto* mi = internal.get())
mi->start();
}
void MidiInput::stop()
{
if (auto* mi = reinterpret_cast<AndroidMidiInput*> (internal))
if (auto* mi = internal.get())
mi->stop();
}
@@ -646,7 +643,7 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifi
if (auto* port = manager.openMidiOutputPortWithID (deviceIdentifier.getIntValue()))
{
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput ({}, deviceIdentifier));
midiOutput->internal = port;
midiOutput->internal.reset (port);
midiOutput->setName (port->getName());
return midiOutput;
@@ -681,13 +678,11 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (int index)
MidiOutput::~MidiOutput()
{
stopBackgroundThread();
delete reinterpret_cast<AndroidMidiOutput*> (internal);
}
void MidiOutput::sendMessageNow (const MidiMessage& message)
{
if (auto* androidMidi = reinterpret_cast<AndroidMidiOutput*>(internal))
if (auto* androidMidi = internal.get())
{
auto* env = getEnv();
auto messageSize = message.getRawDataSize();


+ 35
- 38
source/modules/juce_audio_devices/native/juce_android_Oboe.cpp View File

@@ -1211,9 +1211,13 @@ public:
jmethodID getChannelCountsMethod = env->GetMethodID (deviceClass, "getChannelCounts", "()[I");
jmethodID isSourceMethod = env->GetMethodID (deviceClass, "isSource", "()Z");
auto name = juceString ((jstring) env->CallObjectMethod (device, getProductNameMethod));
name << deviceTypeToString (env->CallIntMethod (device, getTypeMethod));
int id = env->CallIntMethod (device, getIdMethod);
auto deviceTypeString = deviceTypeToString (env->CallIntMethod (device, getTypeMethod));
if (deviceTypeString.isEmpty()) // unknown device
return;
auto name = juceString ((jstring) env->CallObjectMethod (device, getProductNameMethod)) + " " + deviceTypeString;
auto id = env->CallIntMethod (device, getIdMethod);
auto jSampleRates = LocalRef<jintArray> ((jintArray) env->CallObjectMethod (device, getSampleRatesMethod));
auto sampleRates = jintArrayToJuceArray (jSampleRates);
@@ -1222,42 +1226,42 @@ public:
auto channelCounts = jintArrayToJuceArray (jChannelCounts);
int numChannels = channelCounts.isEmpty() ? -1 : channelCounts.getLast();
bool isInput = env->CallBooleanMethod (device, isSourceMethod);
auto isInput = env->CallBooleanMethod (device, isSourceMethod);
auto& devices = isInput ? inputDevices : outputDevices;
devices.add ({ name, id, sampleRates, numChannels });
}
static const char* deviceTypeToString (int type)
static String deviceTypeToString (int type)
{
switch (type)
{
case 0: return "";
case 1: return " built-in earphone speaker";
case 2: return " built-in speaker";
case 3: return " wired headset";
case 4: return " wired headphones";
case 5: return " line analog";
case 6: return " line digital";
case 7: return " Bluetooth device typically used for telephony";
case 8: return " Bluetooth device supporting the A2DP profile";
case 9: return " HDMI";
case 10: return " HDMI audio return channel";
case 11: return " USB device";
case 12: return " USB accessory";
case 13: return " DOCK";
case 14: return " FM";
case 15: return " built-in microphone";
case 16: return " FM tuner";
case 17: return " TV tuner";
case 18: return " telephony";
case 19: return " auxiliary line-level connectors";
case 20: return " IP";
case 21: return " BUS";
case 22: return " USB headset";
case 23: return " hearing aid";
case 24: return " built-in speaker safe";
default: jassertfalse; return ""; // type not supported yet, needs to be added!
case 0: return {};
case 1: return "built-in earphone speaker";
case 2: return "built-in speaker";
case 3: return "wired headset";
case 4: return "wired headphones";
case 5: return "line analog";
case 6: return "line digital";
case 7: return "Bluetooth device typically used for telephony";
case 8: return "Bluetooth device supporting the A2DP profile";
case 9: return "HDMI";
case 10: return "HDMI audio return channel";
case 11: return "USB device";
case 12: return "USB accessory";
case 13: return "DOCK";
case 14: return "FM";
case 15: return "built-in microphone";
case 16: return "FM tuner";
case 17: return "TV tuner";
case 18: return "telephony";
case 19: return "auxiliary line-level connectors";
case 20: return "IP";
case 21: return "BUS";
case 22: return "USB headset";
case 23: return "hearing aid";
case 24: return "built-in speaker safe";
default: jassertfalse; return {}; // type not supported yet, needs to be added!
}
}
@@ -1306,15 +1310,8 @@ public:
const char* const OboeAudioIODevice::oboeTypeName = "Android Oboe";
//==============================================================================
bool isOboeAvailable() { return OboeAudioIODeviceType::isOboeAvailable(); }
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe()
{
return isOboeAvailable() ? new OboeAudioIODeviceType() : nullptr;
}
//==============================================================================
class OboeRealtimeThread : private oboe::AudioStreamCallback
{


+ 4
- 10
source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp View File

@@ -71,12 +71,11 @@ static void destroyObject (SLObjectType object)
(*object)->Destroy (object);
}
template <>
struct ContainerDeletePolicy<const SLObjectItf_* const>
struct SLObjectItfFree
{
static void destroy (SLObjectItf object)
void operator() (SLObjectItf obj) const noexcept
{
destroyObject (object);
destroyObject (obj);
}
};
@@ -112,7 +111,7 @@ private:
ControlBlock() = default;
ControlBlock (SLObjectItf o) : ptr (o) {}
std::unique_ptr<const SLObjectItf_* const> ptr;
std::unique_ptr<const SLObjectItf_* const, SLObjectItfFree> ptr;
};
ReferenceCountedObjectPtr<ControlBlock> cb;
@@ -1121,11 +1120,6 @@ const char* const OpenSLAudioIODevice::openSLTypeName = "Android OpenSL";
//==============================================================================
bool isOpenSLAvailable() { return OpenSLAudioDeviceType::isOpenSLAvailable(); }
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES()
{
return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr;
}
//==============================================================================
class SLRealtimeThread
{


+ 0
- 6
source/modules/juce_audio_devices/native/juce_ios_Audio.cpp View File

@@ -1431,12 +1431,6 @@ void iOSAudioIODeviceType::handleAsyncUpdate()
callDeviceChangeListeners();
}
//==============================================================================
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio()
{
return new iOSAudioIODeviceType();
}
//==============================================================================
AudioSessionHolder::AudioSessionHolder() { nativeSession = [[iOSAudioSessionNative alloc] init: this]; }
AudioSessionHolder::~AudioSessionHolder() { [nativeSession release]; }


+ 0
- 5
source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp View File

@@ -1299,9 +1299,4 @@ AudioIODeviceType* createAudioIODeviceType_ALSA_PCMDevices()
return new ALSAAudioIODeviceType (false, "ALSA");
}
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA()
{
return createAudioIODeviceType_ALSA_PCMDevices();
}
} // namespace juce

+ 26
- 29
source/modules/juce_audio_devices/native/juce_linux_Bela.cpp View File

@@ -24,12 +24,12 @@ namespace juce
{
//==============================================================================
class BelaMidiInput
class MidiInput::Pimpl
{
public:
static Array<BelaMidiInput*> midiInputs;
static Array<Pimpl*> midiInputs;
BelaMidiInput (const String& port, MidiInput* input, MidiInputCallback* callback)
Pimpl (const String& port, MidiInput* input, MidiInputCallback* callback)
: midiInput (input), midiPort (port), midiCallback (callback)
{
jassert (midiCallback != nullptr);
@@ -38,7 +38,7 @@ public:
buffer.resize (32);
}
~BelaMidiInput()
~Pimpl()
{
stop();
midiInputs.removeAllInstancesOf (this);
@@ -76,7 +76,7 @@ public:
}
if (receivedBytes > 0)
pushMidiData (receivedBytes);
pushMidiData ((int) receivedBytes);
}
static Array<MidiDeviceInfo> getDevices (bool input)
@@ -141,7 +141,7 @@ private:
snd_rawmidi_info_t* info;
snd_rawmidi_info_alloca (&info);
snd_rawmidi_info_set_device (info, device);
snd_rawmidi_info_set_device (info, (unsigned int) device);
snd_rawmidi_info_set_stream (info, input ? SND_RAWMIDI_STREAM_INPUT
: SND_RAWMIDI_STREAM_OUTPUT);
@@ -173,10 +173,10 @@ private:
Midi midi;
MidiDataConcatenator concatenator { 512 };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaMidiInput)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
};
Array<BelaMidiInput*> BelaMidiInput::midiInputs;
Array<MidiInput::Pimpl*> MidiInput::Pimpl::midiInputs;
//==============================================================================
@@ -366,7 +366,7 @@ public:
String getLastError() override { return lastError; }
//==============================================================================
int getCurrentBufferSizeSamples() override { return actualBufferSize; }
int getCurrentBufferSizeSamples() override { return (int) actualBufferSize; }
double getCurrentSampleRate() override { return 44100.0; }
int getCurrentBitDepth() override { return 16; }
BigInteger getActiveOutputChannels() const override { BigInteger b; b.setRange (0, actualNumberOfOutputs, true); return b; }
@@ -384,8 +384,8 @@ private:
bool setup (BelaContext& context)
{
actualBufferSize = context.audioFrames;
actualNumberOfInputs = context.audioInChannels + context.analogInChannels;
actualNumberOfOutputs = context.audioOutChannels + context.analogOutChannels;
actualNumberOfInputs = (int) (context.audioInChannels + context.analogInChannels);
actualNumberOfOutputs = (int) (context.audioOutChannels + context.analogOutChannels);
isBelaOpen = true;
firstCallback = true;
@@ -405,7 +405,7 @@ private:
ScopedLock lock (callbackLock);
// Check for and process and midi
for (auto midiInput : BelaMidiInput::midiInputs)
for (auto midiInput : MidiInput::Pimpl::midiInputs)
midiInput->poll();
if (callback != nullptr)
@@ -413,27 +413,29 @@ private:
jassert (context.audioFrames <= actualBufferSize);
jassert ((context.flags & BELA_FLAG_INTERLEAVED) == 0);
using Frames = decltype (context.audioFrames);
// Setup channelInBuffers
for (int ch = 0; ch < actualNumberOfInputs; ++ch)
{
if (ch < analogChannelStart)
channelInBuffer[ch] = &context.audioIn[ch * context.audioFrames];
channelInBuffer[ch] = &context.audioIn[(Frames) ch * context.audioFrames];
else
channelInBuffer[ch] = &context.analogIn[(ch - analogChannelStart) * context.analogFrames];
channelInBuffer[ch] = &context.analogIn[(Frames) (ch - analogChannelStart) * context.analogFrames];
}
// Setup channelOutBuffers
for (int ch = 0; ch < actualNumberOfOutputs; ++ch)
{
if (ch < analogChannelStart)
channelOutBuffer[ch] = &context.audioOut[ch * context.audioFrames];
channelOutBuffer[ch] = &context.audioOut[(Frames) ch * context.audioFrames];
else
channelOutBuffer[ch] = &context.analogOut[(ch - analogChannelStart) * context.audioFrames];
channelOutBuffer[ch] = &context.analogOut[(Frames) (ch - analogChannelStart) * context.audioFrames];
}
callback->audioDeviceIOCallback (channelInBuffer.getData(), actualNumberOfInputs,
channelOutBuffer.getData(), actualNumberOfOutputs,
context.audioFrames);
(int) context.audioFrames);
}
}
@@ -521,25 +523,19 @@ struct BelaAudioIODeviceType : public AudioIODeviceType
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODeviceType)
};
//==============================================================================
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela()
{
return new BelaAudioIODeviceType();
}
//==============================================================================
MidiInput::MidiInput (const String& deviceName, const String& deviceID)
: deviceInfo (deviceName, deviceID)
{
}
MidiInput::~MidiInput() { delete static_cast<BelaMidiInput*> (internal); }
void MidiInput::start() { static_cast<BelaMidiInput*> (internal)->start(); }
void MidiInput::stop() { static_cast<BelaMidiInput*> (internal)->stop(); }
MidiInput::~MidiInput() = default;
void MidiInput::start() { internal->start(); }
void MidiInput::stop() { internal->stop(); }
Array<MidiDeviceInfo> MidiInput::getAvailableDevices()
{
return BelaMidiInput::getDevices (true);
return Pimpl::getDevices (true);
}
MidiDeviceInfo MidiInput::getDefaultDevice()
@@ -553,7 +549,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier
return {};
std::unique_ptr<MidiInput> midiInput (new MidiInput (deviceIdentifier, deviceIdentifier));
midiInput->internal = new BelaMidiInput (deviceIdentifier, midiInput.get(), callback);
midiInput->internal = std::make_unique<Pimpl> (deviceIdentifier, midiInput.get(), callback);
return midiInput;
}
@@ -587,7 +583,8 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (int index, MidiInputCallback*
//==============================================================================
// TODO: Add Bela MidiOutput support
MidiOutput::~MidiOutput() {}
class MidiOutput::Pimpl {};
MidiOutput::~MidiOutput() = default;
void MidiOutput::sendMessageNow (const MidiMessage&) {}
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices() { return {}; }
MidiDeviceInfo MidiOutput::getDefaultDevice() { return {}; }


+ 205
- 163
source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp View File

@@ -36,9 +36,11 @@ static void* juce_loadJackFunction (const char* const name)
#define JUCE_DECL_JACK_FUNCTION(return_type, fn_name, argument_types, arguments) \
return_type fn_name argument_types \
{ \
using ReturnType = return_type; \
typedef return_type (*fn_type) argument_types; \
static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \
return (fn != nullptr) ? ((*fn) arguments) : (return_type) 0; \
jassert (fn != nullptr); \
return (fn != nullptr) ? ((*fn) arguments) : ReturnType(); \
}
#define JUCE_DECL_VOID_JACK_FUNCTION(fn_name, argument_types, arguments) \
@@ -46,30 +48,35 @@ static void* juce_loadJackFunction (const char* const name)
{ \
typedef void (*fn_type) argument_types; \
static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \
jassert (fn != nullptr); \
if (fn != nullptr) (*fn) arguments; \
}
//==============================================================================
JUCE_DECL_JACK_FUNCTION (jack_client_t*, jack_client_open, (const char* client_name, jack_options_t options, jack_status_t* status, ...), (client_name, options, status));
JUCE_DECL_JACK_FUNCTION (int, jack_client_close, (jack_client_t *client), (client));
JUCE_DECL_JACK_FUNCTION (int, jack_activate, (jack_client_t* client), (client));
JUCE_DECL_JACK_FUNCTION (int, jack_deactivate, (jack_client_t* client), (client));
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_buffer_size, (jack_client_t* client), (client));
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_sample_rate, (jack_client_t* client), (client));
JUCE_DECL_VOID_JACK_FUNCTION (jack_on_shutdown, (jack_client_t* client, void (*function)(void* arg), void* arg), (client, function, arg));
JUCE_DECL_JACK_FUNCTION (void* , jack_port_get_buffer, (jack_port_t* port, jack_nframes_t nframes), (port, nframes));
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_port_get_total_latency, (jack_client_t* client, jack_port_t* port), (client, port));
JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_register, (jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size), (client, port_name, port_type, flags, buffer_size));
JUCE_DECL_VOID_JACK_FUNCTION (jack_set_error_function, (void (*func)(const char*)), (func));
JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JackProcessCallback process_callback, void* arg), (client, process_callback, arg));
JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags));
JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port));
JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port));
JUCE_DECL_JACK_FUNCTION (void*, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg));
JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id));
JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port));
JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name));
JUCE_DECL_JACK_FUNCTION (int, jack_set_xrun_callback, (jack_client_t* client, JackXRunCallback xrun_callback, void* arg), (client, xrun_callback, arg));
JUCE_DECL_JACK_FUNCTION (jack_client_t*, jack_client_open, (const char* client_name, jack_options_t options, jack_status_t* status, ...), (client_name, options, status))
JUCE_DECL_JACK_FUNCTION (int, jack_client_close, (jack_client_t *client), (client))
JUCE_DECL_JACK_FUNCTION (int, jack_activate, (jack_client_t* client), (client))
JUCE_DECL_JACK_FUNCTION (int, jack_deactivate, (jack_client_t* client), (client))
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_buffer_size, (jack_client_t* client), (client))
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_sample_rate, (jack_client_t* client), (client))
JUCE_DECL_VOID_JACK_FUNCTION (jack_on_shutdown, (jack_client_t* client, void (*function)(void* arg), void* arg), (client, function, arg))
JUCE_DECL_VOID_JACK_FUNCTION (jack_on_info_shutdown, (jack_client_t* client, JackInfoShutdownCallback function, void* arg), (client, function, arg))
JUCE_DECL_JACK_FUNCTION (void* , jack_port_get_buffer, (jack_port_t* port, jack_nframes_t nframes), (port, nframes))
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_port_get_total_latency, (jack_client_t* client, jack_port_t* port), (client, port))
JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_register, (jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size), (client, port_name, port_type, flags, buffer_size))
JUCE_DECL_VOID_JACK_FUNCTION (jack_set_error_function, (void (*func)(const char*)), (func))
JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JackProcessCallback process_callback, void* arg), (client, process_callback, arg))
JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags))
JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port))
JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port))
JUCE_DECL_JACK_FUNCTION (void*, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg))
JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id))
JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port))
JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name))
JUCE_DECL_JACK_FUNCTION (int, jack_set_xrun_callback, (jack_client_t* client, JackXRunCallback xrun_callback, void* arg), (client, xrun_callback, arg))
JUCE_DECL_JACK_FUNCTION (int, jack_port_flags, (const jack_port_t* port), (port))
JUCE_DECL_JACK_FUNCTION (jack_port_t*, jack_port_by_name, (jack_client_t* client, const char* name), (client, name))
JUCE_DECL_VOID_JACK_FUNCTION (jack_free, (void* ptr), (ptr))
#if JUCE_DEBUG
#define JACK_LOGGING_ENABLED 1
@@ -115,56 +122,56 @@ namespace
struct JackPortIterator
{
JackPortIterator (jack_client_t* const client, const bool forInput)
: ports (nullptr), index (-1)
{
if (client != nullptr)
ports = juce::jack_get_ports (client, nullptr, nullptr,
forInput ? JackPortIsOutput : JackPortIsInput);
// (NB: This looks like it's the wrong way round, but it is correct!)
}
~JackPortIterator()
{
::free (ports);
ports.reset (juce::jack_get_ports (client, nullptr, nullptr,
forInput ? JackPortIsInput : JackPortIsOutput));
}
bool next()
{
if (ports == nullptr || ports [index + 1] == nullptr)
if (ports == nullptr || ports.get()[index + 1] == nullptr)
return false;
name = CharPointer_UTF8 (ports[++index]);
clientName = name.upToFirstOccurrenceOf (":", false, false);
name = CharPointer_UTF8 (ports.get()[++index]);
return true;
}
const char** ports;
int index;
String getClientName() const
{
return name.upToFirstOccurrenceOf (":", false, false);
}
String getChannelName() const
{
return name.fromFirstOccurrenceOf (":", false, false);
}
struct Free
{
void operator() (const char** ptr) const noexcept { juce::jack_free (ptr); }
};
std::unique_ptr<const char*, Free> ports;
int index = -1;
String name;
String clientName;
};
class JackAudioIODeviceType;
static Array<JackAudioIODeviceType*> activeDeviceTypes;
//==============================================================================
class JackAudioIODevice : public AudioIODevice
{
public:
JackAudioIODevice (const String& deviceName,
const String& inId,
const String& outId)
: AudioIODevice (deviceName, "JACK"),
inputId (inId),
outputId (outId),
deviceIsOpen (false),
callback (nullptr),
totalNumberOfInputChannels (0),
totalNumberOfOutputChannels (0)
{
jassert (deviceName.isNotEmpty());
jack_status_t status;
JackAudioIODevice (const String& inName,
const String& outName,
std::function<void()> notifyIn)
: AudioIODevice (outName.isEmpty() ? inName : outName, "JACK"),
inputName (inName),
outputName (outName),
notifyChannelsChanged (std::move (notifyIn))
{
jassert (outName.isNotEmpty() || inName.isNotEmpty());
jack_status_t status = {};
client = juce::jack_client_open (JUCE_JACK_CLIENT_NAME, JackNoStartServer, &status);
if (client == nullptr)
@@ -179,10 +186,10 @@ public:
const StringArray inputChannels (getInputChannelNames());
for (int i = 0; i < inputChannels.size(); ++i)
{
String inputName;
inputName << "in_" << ++totalNumberOfInputChannels;
String inputChannelName;
inputChannelName << "in_" << ++totalNumberOfInputChannels;
inputPorts.add (juce::jack_port_register (client, inputName.toUTF8(),
inputPorts.add (juce::jack_port_register (client, inputChannelName.toUTF8(),
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0));
}
@@ -190,10 +197,10 @@ public:
const StringArray outputChannels (getOutputChannelNames());
for (int i = 0; i < outputChannels.size(); ++i)
{
String outputName;
outputName << "out_" << ++totalNumberOfOutputChannels;
String outputChannelName;
outputChannelName << "out_" << ++totalNumberOfOutputChannels;
outputPorts.add (juce::jack_port_register (client, outputName.toUTF8(),
outputPorts.add (juce::jack_port_register (client, outputChannelName.toUTF8(),
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0));
}
@@ -202,7 +209,7 @@ public:
}
}
~JackAudioIODevice()
~JackAudioIODevice() override
{
close();
if (client != nullptr)
@@ -212,19 +219,19 @@ public:
}
}
StringArray getChannelNames (bool forInput) const
StringArray getChannelNames (const String& clientName, bool forInput) const
{
StringArray names;
for (JackPortIterator i (client, forInput); i.next();)
if (i.clientName == getName())
names.add (i.name.fromFirstOccurrenceOf (":", false, false));
if (i.getClientName() == clientName)
names.add (i.getChannelName());
return names;
}
StringArray getOutputChannelNames() override { return getChannelNames (false); }
StringArray getInputChannelNames() override { return getChannelNames (true); }
StringArray getOutputChannelNames() override { return getChannelNames (outputName, true); }
StringArray getInputChannelNames() override { return getChannelNames (inputName, false); }
Array<double> getAvailableSampleRates() override
{
@@ -241,15 +248,29 @@ public:
Array<int> sizes;
if (client != nullptr)
sizes.add (juce::jack_get_buffer_size (client));
sizes.add (static_cast<int> (juce::jack_get_buffer_size (client)));
return sizes;
}
int getDefaultBufferSize() override { return getCurrentBufferSizeSamples(); }
int getCurrentBufferSizeSamples() override { return client != nullptr ? juce::jack_get_buffer_size (client) : 0; }
double getCurrentSampleRate() override { return client != nullptr ? juce::jack_get_sample_rate (client) : 0; }
int getCurrentBufferSizeSamples() override { return client != nullptr ? static_cast<int> (juce::jack_get_buffer_size (client)) : 0; }
double getCurrentSampleRate() override { return client != nullptr ? static_cast<int> (juce::jack_get_sample_rate (client)) : 0; }
template <typename Fn>
void forEachClientChannel (const String& clientName, bool isInput, Fn&& fn)
{
auto index = 0;
for (JackPortIterator i (client, isInput); i.next();)
{
if (i.getClientName() != clientName)
continue;
fn (i.ports.get()[i.index], index);
index += 1;
}
}
String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
double /* sampleRate */, int /* bufferSizeSamples */) override
@@ -263,38 +284,55 @@ public:
lastError.clear();
close();
xruns = 0;
xruns.store (0, std::memory_order_relaxed);
juce::jack_set_process_callback (client, processCallback, this);
juce::jack_set_port_connect_callback (client, portConnectCallback, this);
juce::jack_on_shutdown (client, shutdownCallback, this);
juce::jack_on_info_shutdown (client, infoShutdownCallback, this);
juce::jack_set_xrun_callback (client, xrunCallback, this);
juce::jack_activate (client);
deviceIsOpen = true;
if (! inputChannels.isZero())
{
for (JackPortIterator i (client, true); i.next();)
forEachClientChannel (inputName, false, [&] (const char* portName, int index)
{
if (inputChannels [i.index] && i.clientName == getName())
{
int error = juce::jack_connect (client, i.ports[i.index], juce::jack_port_name ((jack_port_t*) inputPorts[i.index]));
if (error != 0)
JUCE_JACK_LOG ("Cannot connect input port " + String (i.index) + " (" + i.name + "), error " + String (error));
}
}
if (! inputChannels[index])
return;
jassert (index < inputPorts.size());
const auto* source = portName;
const auto* inputPort = inputPorts[index];
jassert (juce::jack_port_flags (juce::jack_port_by_name (client, source)) & JackPortIsOutput);
jassert (juce::jack_port_flags (inputPort) & JackPortIsInput);
auto error = juce::jack_connect (client, source, juce::jack_port_name (inputPort));
if (error != 0)
JUCE_JACK_LOG ("Cannot connect input port " + String (index) + " (" + portName + "), error " + String (error));
});
}
if (! outputChannels.isZero())
{
for (JackPortIterator i (client, false); i.next();)
forEachClientChannel (outputName, true, [&] (const char* portName, int index)
{
if (outputChannels [i.index] && i.clientName == getName())
{
int error = juce::jack_connect (client, juce::jack_port_name ((jack_port_t*) outputPorts[i.index]), i.ports[i.index]);
if (error != 0)
JUCE_JACK_LOG ("Cannot connect output port " + String (i.index) + " (" + i.name + "), error " + String (error));
}
}
if (! outputChannels[index])
return;
jassert (index < outputPorts.size());
const auto* outputPort = outputPorts[index];
const auto* destination = portName;
jassert (juce::jack_port_flags (outputPort) & JackPortIsOutput);
jassert (juce::jack_port_flags (juce::jack_port_by_name (client, destination)) & JackPortIsInput);
auto error = juce::jack_connect (client, juce::jack_port_name (outputPort), destination);
if (error != 0)
JUCE_JACK_LOG ("Cannot connect output port " + String (index) + " (" + portName + "), error " + String (error));
});
}
updateActivePorts();
@@ -308,12 +346,15 @@ public:
if (client != nullptr)
{
juce::jack_deactivate (client);
const auto result = juce::jack_deactivate (client);
jassert (result == 0);
ignoreUnused (result);
juce::jack_set_xrun_callback (client, xrunCallback, nullptr);
juce::jack_set_process_callback (client, processCallback, nullptr);
juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr);
juce::jack_on_shutdown (client, shutdownCallback, nullptr);
juce::jack_on_info_shutdown (client, infoShutdownCallback, nullptr);
}
deviceIsOpen = false;
@@ -347,7 +388,7 @@ public:
bool isPlaying() override { return callback != nullptr; }
int getCurrentBitDepth() override { return 32; }
String getLastError() override { return lastError; }
int getXRunCount() const noexcept override { return xruns; }
int getXRunCount() const noexcept override { return xruns.load (std::memory_order_relaxed); }
BigInteger getActiveOutputChannels() const override { return activeOutputChannels; }
BigInteger getActiveInputChannels() const override { return activeInputChannels; }
@@ -357,7 +398,7 @@ public:
int latency = 0;
for (int i = 0; i < outputPorts.size(); i++)
latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) outputPorts [i]));
latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, outputPorts[i]));
return latency;
}
@@ -367,14 +408,36 @@ public:
int latency = 0;
for (int i = 0; i < inputPorts.size(); i++)
latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) inputPorts [i]));
latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, inputPorts[i]));
return latency;
}
String inputId, outputId;
String inputName, outputName;
private:
//==============================================================================
class MainThreadDispatcher : private AsyncUpdater
{
public:
explicit MainThreadDispatcher (JackAudioIODevice& device) : ref (device) {}
~MainThreadDispatcher() override { cancelPendingUpdate(); }
void updateActivePorts()
{
if (MessageManager::getInstance()->isThisTheMessageThread())
handleAsyncUpdate();
else
triggerAsyncUpdate();
}
private:
void handleAsyncUpdate() override { ref.updateActivePorts(); }
JackAudioIODevice& ref;
};
//==============================================================================
void process (const int numSamples)
{
int numActiveInChans = 0, numActiveOutChans = 0;
@@ -382,17 +445,17 @@ private:
for (int i = 0; i < totalNumberOfInputChannels; ++i)
{
if (activeInputChannels[i])
if (jack_default_audio_sample_t* in
= (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) inputPorts.getUnchecked(i), numSamples))
inChans [numActiveInChans++] = (float*) in;
if (auto* in = (jack_default_audio_sample_t*) juce::jack_port_get_buffer (inputPorts.getUnchecked (i),
static_cast<jack_nframes_t> (numSamples)))
inChans[numActiveInChans++] = (float*) in;
}
for (int i = 0; i < totalNumberOfOutputChannels; ++i)
{
if (activeOutputChannels[i])
if (jack_default_audio_sample_t* out
= (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) outputPorts.getUnchecked(i), numSamples))
outChans [numActiveOutChans++] = (float*) out;
if (auto* out = (jack_default_audio_sample_t*) juce::jack_port_get_buffer (outputPorts.getUnchecked (i),
static_cast<jack_nframes_t> (numSamples)))
outChans[numActiveOutChans++] = (float*) out;
}
const ScopedLock sl (callbackLock);
@@ -406,14 +469,14 @@ private:
else
{
for (int i = 0; i < numActiveOutChans; ++i)
zeromem (outChans[i], sizeof (float) * numSamples);
zeromem (outChans[i], static_cast<size_t> (numSamples) * sizeof (float));
}
}
static int processCallback (jack_nframes_t nframes, void* callbackArgument)
{
if (callbackArgument != nullptr)
((JackAudioIODevice*) callbackArgument)->process (nframes);
((JackAudioIODevice*) callbackArgument)->process (static_cast<int> (nframes));
return 0;
}
@@ -431,11 +494,11 @@ private:
BigInteger newOutputChannels, newInputChannels;
for (int i = 0; i < outputPorts.size(); ++i)
if (juce::jack_port_connected ((jack_port_t*) outputPorts.getUnchecked(i)))
if (juce::jack_port_connected (outputPorts.getUnchecked (i)))
newOutputChannels.setBit (i);
for (int i = 0; i < inputPorts.size(); ++i)
if (juce::jack_port_connected ((jack_port_t*) inputPorts.getUnchecked(i)))
if (juce::jack_port_connected (inputPorts.getUnchecked (i)))
newInputChannels.setBit (i);
if (newOutputChannels != activeOutputChannels
@@ -451,14 +514,15 @@ private:
if (oldCallback != nullptr)
start (oldCallback);
sendDeviceChangedCallback();
if (notifyChannelsChanged != nullptr)
notifyChannelsChanged();
}
}
static void portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg)
{
if (JackAudioIODevice* device = static_cast<JackAudioIODevice*> (arg))
device->updateActivePorts();
device->mainThreadDispatcher.updateActivePorts();
}
static void threadInitCallback (void* /* callbackArgument */)
@@ -477,81 +541,76 @@ private:
}
}
static void infoShutdownCallback (jack_status_t code, const char* reason, void* arg)
{
jassert (code == 0);
ignoreUnused (code);
JUCE_JACK_LOG ("Shutting down with message:");
JUCE_JACK_LOG (reason);
ignoreUnused (reason);
shutdownCallback (arg);
}
static void errorCallback (const char* msg)
{
JUCE_JACK_LOG ("JackAudioIODevice::errorCallback " + String (msg));
ignoreUnused (msg);
}
static void sendDeviceChangedCallback();
bool deviceIsOpen;
jack_client_t* client;
bool deviceIsOpen = false;
jack_client_t* client = nullptr;
String lastError;
AudioIODeviceCallback* callback;
AudioIODeviceCallback* callback = nullptr;
CriticalSection callbackLock;
HeapBlock<float*> inChans, outChans;
int totalNumberOfInputChannels;
int totalNumberOfOutputChannels;
Array<void*> inputPorts, outputPorts;
int totalNumberOfInputChannels = 0;
int totalNumberOfOutputChannels = 0;
Array<jack_port_t*> inputPorts, outputPorts;
BigInteger activeInputChannels, activeOutputChannels;
int xruns;
};
std::atomic<int> xruns { 0 };
std::function<void()> notifyChannelsChanged;
MainThreadDispatcher mainThreadDispatcher { *this };
};
//==============================================================================
class JackAudioIODeviceType;
class JackAudioIODeviceType : public AudioIODeviceType
{
public:
JackAudioIODeviceType()
: AudioIODeviceType ("JACK"),
hasScanned (false)
{
activeDeviceTypes.add (this);
}
~JackAudioIODeviceType()
{
activeDeviceTypes.removeFirstMatchingValue (this);
}
: AudioIODeviceType ("JACK")
{}
void scanForDevices()
{
hasScanned = true;
inputNames.clear();
inputIds.clear();
outputNames.clear();
outputIds.clear();
if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so.0", RTLD_LAZY);
if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY);
if (juce_libjackHandle == nullptr) return;
jack_status_t status;
jack_status_t status = {};
// open a dummy client
if (jack_client_t* const client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status))
if (auto* const client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status))
{
// scan for output devices
for (JackPortIterator i (client, false); i.next();)
{
if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (i.clientName))
{
inputNames.add (i.clientName);
inputIds.add (i.ports [i.index]);
}
}
if (i.getClientName() != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (i.getClientName()))
inputNames.add (i.getClientName());
// scan for input devices
for (JackPortIterator i (client, true); i.next();)
{
if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (i.clientName))
{
outputNames.add (i.clientName);
outputIds.add (i.ports [i.index]);
}
}
if (i.getClientName() != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (i.getClientName()))
outputNames.add (i.getClientName());
juce::jack_client_close (client);
}
@@ -580,8 +639,8 @@ public:
jassert (hasScanned); // need to call scanForDevices() before doing this
if (JackAudioIODevice* d = dynamic_cast<JackAudioIODevice*> (device))
return asInput ? inputIds.indexOf (d->inputId)
: outputIds.indexOf (d->outputId);
return asInput ? inputNames.indexOf (d->inputName)
: outputNames.indexOf (d->outputName);
return -1;
}
@@ -595,34 +654,17 @@ public:
const int outputIndex = outputNames.indexOf (outputDeviceName);
if (inputIndex >= 0 || outputIndex >= 0)
return new JackAudioIODevice (outputIndex >= 0 ? outputDeviceName
: inputDeviceName,
inputIds [inputIndex],
outputIds [outputIndex]);
return new JackAudioIODevice (inputDeviceName, outputDeviceName,
[this] { callDeviceChangeListeners(); });
return nullptr;
}
void portConnectionChange() { callDeviceChangeListeners(); }
private:
StringArray inputNames, outputNames, inputIds, outputIds;
bool hasScanned;
StringArray inputNames, outputNames;
bool hasScanned = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType)
};
void JackAudioIODevice::sendDeviceChangedCallback()
{
for (int i = activeDeviceTypes.size(); --i >= 0;)
if (JackAudioIODeviceType* d = activeDeviceTypes[i])
d->portConnectionChange();
}
//==============================================================================
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK()
{
return new JackAudioIODeviceType();
}
} // namespace juce

+ 32
- 14
source/modules/juce_audio_devices/native/juce_linux_Midi.cpp View File

@@ -25,10 +25,6 @@ namespace juce
#if JUCE_ALSA
//==============================================================================
namespace
{
//==============================================================================
class AlsaClient : public ReferenceCountedObject
{
@@ -453,9 +449,23 @@ static AlsaClient::Port* iterateMidiDevices (bool forInput,
return port;
}
} // namespace
struct AlsaPortPtr
{
explicit AlsaPortPtr (AlsaClient::Port* p)
: ptr (p) {}
~AlsaPortPtr() noexcept { AlsaClient::getInstance()->deletePort (ptr); }
AlsaClient::Port* ptr = nullptr;
};
//==============================================================================
class MidiInput::Pimpl : public AlsaPortPtr
{
public:
using AlsaPortPtr::AlsaPortPtr;
};
Array<MidiDeviceInfo> MidiInput::getAvailableDevices()
{
Array<MidiDeviceInfo> devices;
@@ -485,7 +495,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier
std::unique_ptr<MidiInput> midiInput (new MidiInput (port->portName, deviceIdentifier));
port->setupInput (midiInput.get(), callback);
midiInput->internal = port;
midiInput->internal = std::make_unique<Pimpl> (port);
return midiInput;
}
@@ -501,7 +511,7 @@ std::unique_ptr<MidiInput> MidiInput::createNewDevice (const String& deviceName,
std::unique_ptr<MidiInput> midiInput (new MidiInput (deviceName, getFormattedPortIdentifier (client->getId(), port->portId)));
port->setupInput (midiInput.get(), callback);
midiInput->internal = port;
midiInput->internal = std::make_unique<Pimpl> (port);
return midiInput;
}
@@ -536,20 +546,25 @@ MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier)
MidiInput::~MidiInput()
{
stop();
AlsaClient::getInstance()->deletePort (static_cast<AlsaClient::Port*> (internal));
}
void MidiInput::start()
{
static_cast<AlsaClient::Port*> (internal)->enableCallback (true);
internal->ptr->enableCallback (true);
}
void MidiInput::stop()
{
static_cast<AlsaClient::Port*> (internal)->enableCallback (false);
internal->ptr->enableCallback (false);
}
//==============================================================================
class MidiOutput::Pimpl : public AlsaPortPtr
{
public:
using AlsaPortPtr::AlsaPortPtr;
};
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices()
{
Array<MidiDeviceInfo> devices;
@@ -577,7 +592,7 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifi
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (port->portName, deviceIdentifier));
port->setupOutput();
midiOutput->internal = port;
midiOutput->internal = std::make_unique<Pimpl> (port);
return midiOutput;
}
@@ -593,7 +608,7 @@ std::unique_ptr<MidiOutput> MidiOutput::createNewDevice (const String& deviceNam
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (deviceName, getFormattedPortIdentifier (client->getId(), port->portId)));
port->setupOutput();
midiOutput->internal = port;
midiOutput->internal = std::make_unique<Pimpl> (port);
return midiOutput;
}
@@ -623,17 +638,18 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (int index)
MidiOutput::~MidiOutput()
{
stopBackgroundThread();
AlsaClient::getInstance()->deletePort (static_cast<AlsaClient::Port*> (internal));
}
void MidiOutput::sendMessageNow (const MidiMessage& message)
{
static_cast<AlsaClient::Port*> (internal)->sendMessageNow (message);
internal->ptr->sendMessageNow (message);
}
//==============================================================================
#else
class MidiInput::Pimpl {};
// (These are just stub functions if ALSA is unavailable...)
MidiInput::MidiInput (const String& deviceName, const String& deviceID)
: deviceInfo (deviceName, deviceID)
@@ -651,6 +667,8 @@ StringArray MidiInput::getDevices()
int MidiInput::getDefaultDeviceIndex() { return 0;}
std::unique_ptr<MidiInput> MidiInput::openDevice (int, MidiInputCallback*) { return {}; }
class MidiOutput::Pimpl {};
MidiOutput::~MidiOutput() {}
void MidiOutput::sendMessageNow (const MidiMessage&) {}
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices() { return {}; }


+ 0
- 6
source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp View File

@@ -2234,12 +2234,6 @@ private:
};
//==============================================================================
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio()
{
return new CoreAudioClasses::CoreAudioIODeviceType();
}
#undef JUCE_COREAUDIOLOG
} // namespace juce

+ 26
- 17
source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp View File

@@ -393,6 +393,12 @@ namespace CoreMidiHelpers
}
}
class MidiInput::Pimpl : public CoreMidiHelpers::MidiPortAndCallback
{
public:
using MidiPortAndCallback::MidiPortAndCallback;
};
//==============================================================================
Array<MidiDeviceInfo> MidiInput::getAvailableDevices()
{
@@ -424,7 +430,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier
if (CHECK_ERROR (MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &cfName.cfString)))
{
MIDIPortRef port;
auto mpc = std::make_unique<MidiPortAndCallback> (*callback);
auto mpc = std::make_unique<Pimpl> (*callback);
if (CHECK_ERROR (MIDIInputPortCreate (client, cfName.cfString, midiInputProc, mpc.get(), &port)))
{
@@ -435,10 +441,11 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier
std::unique_ptr<MidiInput> midiInput (new MidiInput (endpointInfo.name, endpointInfo.identifier));
mpc->input = midiInput.get();
midiInput->internal = mpc.get();
auto* ptr = mpc.get();
midiInput->internal = std::move (mpc);
const ScopedLock sl (callbackLock);
activeCallbacks.add (mpc.release());
activeCallbacks.add (ptr);
return midiInput;
}
@@ -462,7 +469,7 @@ std::unique_ptr<MidiInput> MidiInput::createNewDevice (const String& deviceName,
if (auto client = getGlobalMidiClient())
{
auto mpc = std::make_unique<MidiPortAndCallback> (*callback);
auto mpc = std::make_unique<Pimpl> (*callback);
mpc->active = false;
MIDIEndpointRef endpoint;
@@ -491,10 +498,11 @@ std::unique_ptr<MidiInput> MidiInput::createNewDevice (const String& deviceName,
std::unique_ptr<MidiInput> midiInput (new MidiInput (deviceName, String (deviceIdentifier)));
mpc->input = midiInput.get();
midiInput->internal = mpc.get();
auto* ptr = mpc.get();
midiInput->internal = std::move (mpc);
const ScopedLock sl (callbackLock);
activeCallbacks.add (mpc.release());
activeCallbacks.add (ptr);
return midiInput;
}
@@ -529,24 +537,27 @@ MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier)
{
}
MidiInput::~MidiInput()
{
delete static_cast<CoreMidiHelpers::MidiPortAndCallback*> (internal);
}
MidiInput::~MidiInput() = default;
void MidiInput::start()
{
const ScopedLock sl (CoreMidiHelpers::callbackLock);
static_cast<CoreMidiHelpers::MidiPortAndCallback*> (internal)->active = true;
internal->active = true;
}
void MidiInput::stop()
{
const ScopedLock sl (CoreMidiHelpers::callbackLock);
static_cast<CoreMidiHelpers::MidiPortAndCallback*> (internal)->active = false;
internal->active = false;
}
//==============================================================================
class MidiOutput::Pimpl : public CoreMidiHelpers::MidiPortAndEndpoint
{
public:
using MidiPortAndEndpoint::MidiPortAndEndpoint;
};
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices()
{
return CoreMidiHelpers::findDevices (false);
@@ -581,7 +592,7 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifi
if (CHECK_ERROR (MIDIOutputPortCreate (client, cfName.cfString, &port)))
{
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (endpointInfo.name, endpointInfo.identifier));
midiOutput->internal = new MidiPortAndEndpoint (port, endpoint);
midiOutput->internal = std::make_unique<Pimpl> (port, endpoint);
return midiOutput;
}
@@ -622,7 +633,7 @@ std::unique_ptr<MidiOutput> MidiOutput::createNewDevice (const String& deviceNam
if (CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier)))
{
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (deviceName, String (deviceIdentifier)));
midiOutput->internal = new MidiPortAndEndpoint (0, endpoint);
midiOutput->internal = std::make_unique<Pimpl> ((UInt32) 0, endpoint);
return midiOutput;
}
@@ -655,8 +666,6 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (int index)
MidiOutput::~MidiOutput()
{
stopBackgroundThread();
delete static_cast<CoreMidiHelpers::MidiPortAndEndpoint*> (internal);
}
void MidiOutput::sendMessageNow (const MidiMessage& message)
@@ -715,7 +724,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message)
return;
}
static_cast<CoreMidiHelpers::MidiPortAndEndpoint*> (internal)->send (packetToSend);
internal->send (packetToSend);
}
#undef CHECK_ERROR


+ 8
- 13
source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp View File

@@ -504,7 +504,7 @@ public:
{
inBuffers[n] = ioBufferSpace + (currentBlockSizeSamples * n);
ASIOChannelInfo channelInfo = { 0 };
ASIOChannelInfo channelInfo = {};
channelInfo.channel = i;
channelInfo.isInput = 1;
asioObject->getChannelInfo (&channelInfo);
@@ -526,7 +526,7 @@ public:
{
outBuffers[n] = ioBufferSpace + (currentBlockSizeSamples * (numActiveInputChans + n));
ASIOChannelInfo channelInfo = { 0 };
ASIOChannelInfo channelInfo = {};
channelInfo.channel = i;
channelInfo.isInput = 0;
asioObject->getChannelInfo (&channelInfo);
@@ -673,10 +673,10 @@ public:
lastCallback->audioDeviceStopped();
}
String getLastError() { return error; }
bool hasControlPanel() const { return true; }
String getLastError() override { return error; }
bool hasControlPanel() const override { return true; }
bool showControlPanel()
bool showControlPanel() override
{
JUCE_ASIO_LOG ("showing control panel");
@@ -767,7 +767,7 @@ private:
bool deviceIsOpen = false, isStarted = false, buffersCreated = false;
std::atomic<bool> calledback { false };
bool littleEndian = false, postOutput = true, needToReset = false;
bool postOutput = true, needToReset = false;
bool insideControlPanelModalLoop = false;
bool shouldUsePreferredSize = false;
int xruns = 0;
@@ -785,7 +785,7 @@ private:
String getChannelName (int index, bool isInput) const
{
ASIOChannelInfo channelInfo = { 0 };
ASIOChannelInfo channelInfo = {};
channelInfo.channel = index;
channelInfo.isInput = isInput ? 1 : 0;
asioObject->getChannelInfo (&channelInfo);
@@ -1065,7 +1065,7 @@ private:
for (int i = 0; i < totalNumOutputChans; ++i)
{
ASIOChannelInfo channelInfo = { 0 };
ASIOChannelInfo channelInfo = {};
channelInfo.channel = i;
channelInfo.isInput = 0;
asioObject->getChannelInfo (&channelInfo);
@@ -1640,9 +1640,4 @@ void sendASIODeviceChangeToListeners (ASIOAudioIODeviceType* type)
type->sendDeviceChangeToListeners();
}
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO()
{
return new ASIOAudioIODeviceType();
}
} // namespace juce

+ 0
- 6
source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp View File

@@ -1282,10 +1282,4 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType)
};
//==============================================================================
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound()
{
return new DSoundAudioIODeviceType();
}
} // namespace juce

+ 38
- 40
source/modules/juce_audio_devices/native/juce_win32_Midi.cpp View File

@@ -29,37 +29,39 @@
namespace juce
{
struct MidiServiceType
class MidiInput::Pimpl
{
struct InputWrapper
{
virtual ~InputWrapper() {}
public:
virtual ~Pimpl() noexcept = default;
virtual String getDeviceIdentifier() = 0;
virtual String getDeviceName() = 0;
virtual String getDeviceIdentifier() = 0;
virtual String getDeviceName() = 0;
virtual void start() = 0;
virtual void stop() = 0;
};
virtual void start() = 0;
virtual void stop() = 0;
};
struct OutputWrapper
{
virtual ~OutputWrapper() {}
class MidiOutput::Pimpl
{
public:
virtual ~Pimpl() noexcept = default;
virtual String getDeviceIdentifier() = 0;
virtual String getDeviceName() = 0;
virtual String getDeviceIdentifier() = 0;
virtual String getDeviceName() = 0;
virtual void sendMessageNow (const MidiMessage&) = 0;
};
virtual void sendMessageNow (const MidiMessage&) = 0;
};
MidiServiceType() {}
virtual ~MidiServiceType() {}
struct MidiServiceType
{
MidiServiceType() = default;
virtual ~MidiServiceType() noexcept = default;
virtual Array<MidiDeviceInfo> getAvailableDevices (bool) = 0;
virtual MidiDeviceInfo getDefaultDevice (bool) = 0;
virtual InputWrapper* createInputWrapper (MidiInput&, const String&, MidiInputCallback&) = 0;
virtual OutputWrapper* createOutputWrapper (const String&) = 0;
virtual MidiInput::Pimpl* createInputWrapper (MidiInput&, const String&, MidiInputCallback&) = 0;
virtual MidiOutput::Pimpl* createOutputWrapper (const String&) = 0;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiServiceType)
};
@@ -82,12 +84,12 @@ struct Win32MidiService : public MidiServiceType,
: Win32OutputWrapper::getDefaultDevice();
}
InputWrapper* createInputWrapper (MidiInput& input, const String& deviceIdentifier, MidiInputCallback& callback) override
MidiInput::Pimpl* createInputWrapper (MidiInput& input, const String& deviceIdentifier, MidiInputCallback& callback) override
{
return new Win32InputWrapper (*this, input, deviceIdentifier, callback);
}
OutputWrapper* createOutputWrapper (const String& deviceIdentifier) override
MidiOutput::Pimpl* createOutputWrapper (const String& deviceIdentifier) override
{
return new Win32OutputWrapper (*this, deviceIdentifier);
}
@@ -384,7 +386,7 @@ private:
}
};
struct Win32InputWrapper : public InputWrapper,
struct Win32InputWrapper : public MidiInput::Pimpl,
public Win32MidiDeviceQuery<Win32InputWrapper>
{
Win32InputWrapper (Win32MidiService& parentService, MidiInput& midiInput, const String& deviceIdentifier, MidiInputCallback& c)
@@ -508,7 +510,7 @@ private:
};
//==============================================================================
struct Win32OutputWrapper : public OutputWrapper,
struct Win32OutputWrapper : public MidiOutput::Pimpl,
public Win32MidiDeviceQuery<Win32OutputWrapper>
{
Win32OutputWrapper (Win32MidiService& p, const String& deviceIdentifier)
@@ -763,12 +765,12 @@ public:
: outputDeviceWatcher->getDefaultDevice();
}
InputWrapper* createInputWrapper (MidiInput& input, const String& deviceIdentifier, MidiInputCallback& callback) override
MidiInput::Pimpl* createInputWrapper (MidiInput& input, const String& deviceIdentifier, MidiInputCallback& callback) override
{
return new WinRTInputWrapper (*this, input, deviceIdentifier, callback);
}
OutputWrapper* createOutputWrapper (const String& deviceIdentifier) override
MidiOutput::Pimpl* createOutputWrapper (const String& deviceIdentifier) override
{
return new WinRTOutputWrapper (*this, deviceIdentifier);
}
@@ -1554,7 +1556,7 @@ private:
};
//==============================================================================
struct WinRTInputWrapper final : public InputWrapper,
struct WinRTInputWrapper final : public MidiInput::Pimpl,
private WinRTIOWrapper<IMidiInPortStatics, IMidiInPort>
{
@@ -1708,7 +1710,7 @@ private:
};
//==============================================================================
struct WinRTOutputWrapper final : public OutputWrapper,
struct WinRTOutputWrapper final : public MidiOutput::Pimpl,
private WinRTIOWrapper <IMidiOutPortStatics, IMidiOutPort>
{
WinRTOutputWrapper (WinRTMidiService& service, const String& deviceIdentifier)
@@ -1865,7 +1867,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier
return {};
std::unique_ptr<MidiInput> in (new MidiInput ({}, deviceIdentifier));
std::unique_ptr<MidiServiceType::InputWrapper> wrapper;
std::unique_ptr<Pimpl> wrapper;
try
{
@@ -1877,7 +1879,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier
}
in->setName (wrapper->getDeviceName());
in->internal = wrapper.release();
in->internal = std::move (wrapper);
return in;
}
@@ -1907,13 +1909,10 @@ MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier)
{
}
MidiInput::~MidiInput()
{
delete static_cast<MidiServiceType::InputWrapper*> (internal);
}
MidiInput::~MidiInput() = default;
void MidiInput::start() { static_cast<MidiServiceType::InputWrapper*> (internal)->start(); }
void MidiInput::stop() { static_cast<MidiServiceType::InputWrapper*> (internal)->stop(); }
void MidiInput::start() { internal->start(); }
void MidiInput::stop() { internal->stop(); }
//==============================================================================
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices()
@@ -1931,7 +1930,7 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifi
if (deviceIdentifier.isEmpty())
return {};
std::unique_ptr<MidiServiceType::OutputWrapper> wrapper;
std::unique_ptr<Pimpl> wrapper;
try
{
@@ -1945,7 +1944,7 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifi
std::unique_ptr<MidiOutput> out;
out.reset (new MidiOutput (wrapper->getDeviceName(), deviceIdentifier));
out->internal = wrapper.release();
out->internal = std::move (wrapper);
return out;
}
@@ -1973,12 +1972,11 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (int index)
MidiOutput::~MidiOutput()
{
stopBackgroundThread();
delete static_cast<MidiServiceType::OutputWrapper*> (internal);
}
void MidiOutput::sendMessageNow (const MidiMessage& message)
{
static_cast<MidiServiceType::OutputWrapper*> (internal)->sendMessageNow (message);
internal->sendMessageNow (message);
}
} // namespace juce

+ 324
- 125
source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp View File

@@ -208,6 +208,29 @@ enum AUDCLNT_SHAREMODE
AUDCLNT_SHAREMODE_EXCLUSIVE
};
enum AUDIO_STREAM_CATEGORY
{
AudioCategory_Other = 0,
AudioCategory_ForegroundOnlyMedia,
AudioCategory_BackgroundCapableMedia,
AudioCategory_Communications,
AudioCategory_Alerts,
AudioCategory_SoundEffects,
AudioCategory_GameEffects,
AudioCategory_GameMedia,
AudioCategory_GameChat,
AudioCategory_Speech,
AudioCategory_Movie,
AudioCategory_Media
};
struct AudioClientProperties
{
UINT32 cbSize;
BOOL bIsOffload;
AUDIO_STREAM_CATEGORY eCategory;
};
JUCE_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2")
{
JUCE_COMCALL Initialize (AUDCLNT_SHAREMODE, DWORD, REFERENCE_TIME, REFERENCE_TIME, const WAVEFORMATEX*, LPCGUID) = 0;
@@ -224,6 +247,20 @@ JUCE_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2")
JUCE_COMCALL GetService (REFIID, void**) = 0;
};
JUCE_COMCLASS (IAudioClient2, "726778CD-F60A-4eda-82DE-E47610CD78AA") : public IAudioClient
{
JUCE_COMCALL IsOffloadCapable (AUDIO_STREAM_CATEGORY, BOOL*) = 0;
JUCE_COMCALL SetClientProperties (const AudioClientProperties*) = 0;
JUCE_COMCALL GetBufferSizeLimits (const WAVEFORMATEX*, BOOL, REFERENCE_TIME*, REFERENCE_TIME*) = 0;
};
JUCE_COMCLASS (IAudioClient3, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") : public IAudioClient2
{
JUCE_COMCALL GetSharedModeEnginePeriod (const WAVEFORMATEX*, UINT32*, UINT32*, UINT32*, UINT32*) = 0;
JUCE_COMCALL GetCurrentSharedModeEnginePeriod (WAVEFORMATEX**, UINT32*) = 0;
JUCE_COMCALL InitializeSharedAudioStream (DWORD, UINT32, const WAVEFORMATEX*, LPCGUID) = 0;
};
JUCE_IUNKNOWNCLASS (IAudioCaptureClient, "C8ADBD64-E71E-48a0-A4DE-185C395CD317")
{
JUCE_COMCALL GetBuffer (BYTE**, UINT32*, DWORD*, UINT64*, UINT64*) = 0;
@@ -322,87 +359,76 @@ String getDeviceID (IMMDevice* device)
return s;
}
EDataFlow getDataFlow (const ComSmartPtr<IMMDevice>& device)
static EDataFlow getDataFlow (const ComSmartPtr<IMMDevice>& device)
{
EDataFlow flow = eRender;
ComSmartPtr<IMMEndpoint> endPoint;
if (check (device.QueryInterface (endPoint)))
(void) check (endPoint->GetDataFlow (&flow));
if (auto endpoint = device.getInterface<IMMEndpoint>())
(void) check (endpoint->GetDataFlow (&flow));
return flow;
}
int refTimeToSamples (const REFERENCE_TIME& t, double sampleRate) noexcept
static int refTimeToSamples (const REFERENCE_TIME& t, double sampleRate) noexcept
{
return roundToInt (sampleRate * ((double) t) * 0.0000001);
}
REFERENCE_TIME samplesToRefTime (int numSamples, double sampleRate) noexcept
static REFERENCE_TIME samplesToRefTime (int numSamples, double sampleRate) noexcept
{
return (REFERENCE_TIME) ((numSamples * 10000.0 * 1000.0 / sampleRate) + 0.5);
}
void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* src) noexcept
static void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* src) noexcept
{
memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE)
: sizeof (WAVEFORMATEX));
}
static bool isExclusiveMode (WASAPIDeviceMode deviceMode) noexcept
{
return deviceMode == WASAPIDeviceMode::exclusive;
}
static bool isLowLatencyMode (WASAPIDeviceMode deviceMode) noexcept
{
return deviceMode == WASAPIDeviceMode::sharedLowLatency;
}
static bool supportsSampleRateConversion (WASAPIDeviceMode deviceMode) noexcept
{
return deviceMode == WASAPIDeviceMode::shared;
}
//==============================================================================
class WASAPIDeviceBase
{
public:
WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode)
: device (d), useExclusiveMode (exclusiveMode)
WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& d, WASAPIDeviceMode mode)
: device (d),
deviceMode (mode)
{
clientEvent = CreateEvent (nullptr, false, false, nullptr);
ComSmartPtr<IAudioClient> tempClient (createClient());
if (tempClient == nullptr)
return;
REFERENCE_TIME defaultPeriod, minPeriod;
if (! check (tempClient->GetDevicePeriod (&defaultPeriod, &minPeriod)))
return;
WAVEFORMATEXTENSIBLE format;
WAVEFORMATEX* mixFormat = nullptr;
if (! check (tempClient->GetMixFormat (&mixFormat)))
if (! getClientMixFormat (tempClient, format))
return;
WAVEFORMATEXTENSIBLE format;
copyWavFormat (format, mixFormat);
CoTaskMemFree (mixFormat);
actualNumChannels = numChannels = format.Format.nChannels;
defaultSampleRate = format.Format.nSamplesPerSec;
minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate);
defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate);
mixFormatChannelMask = format.dwChannelMask;
rates.addUsingDefaultSort (defaultSampleRate);
mixFormatChannelMask = format.dwChannelMask;
if (useExclusiveMode
&& findSupportedFormat (tempClient, defaultSampleRate, format.dwChannelMask, format))
{
// Got a format that is supported by the device so we can ask what sample rates are supported (in whatever format)
}
for (auto rate : { 8000, 11025, 16000, 22050, 32000,
44100, 48000, 88200, 96000, 176400,
192000, 352800, 384000, 705600, 768000 })
{
if (rates.contains (rate))
continue;
format.Format.nSamplesPerSec = (DWORD) rate;
format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8);
if (isExclusiveMode (deviceMode))
findSupportedFormat (tempClient, defaultSampleRate, mixFormatChannelMask, format);
if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE
: AUDCLNT_SHAREMODE_SHARED,
(WAVEFORMATEX*) &format, 0)))
if (! rates.contains (rate))
rates.addUsingDefaultSort (rate);
}
querySupportedBufferSizes (format, tempClient);
querySupportedSampleRates (format, tempClient);
}
virtual ~WASAPIDeviceBase()
@@ -487,11 +513,14 @@ public:
//==============================================================================
ComSmartPtr<IMMDevice> device;
ComSmartPtr<IAudioClient> client;
WASAPIDeviceMode deviceMode;
double sampleRate = 0, defaultSampleRate = 0;
int numChannels = 0, actualNumChannels = 0;
int minBufferSize = 0, defaultBufferSize = 0, latencySamples = 0;
int lowLatencyBufferSizeMultiple = 0, lowLatencyMaxBufferSize = 0;
DWORD mixFormatChannelMask = 0;
const bool useExclusiveMode;
Array<double> rates;
HANDLE clientEvent = {};
BigInteger channels;
@@ -582,6 +611,84 @@ private:
return newClient;
}
static bool getClientMixFormat (ComSmartPtr<IAudioClient>& client, WAVEFORMATEXTENSIBLE& format)
{
WAVEFORMATEX* mixFormat = nullptr;
if (! check (client->GetMixFormat (&mixFormat)))
return false;
copyWavFormat (format, mixFormat);
CoTaskMemFree (mixFormat);
return true;
}
//==============================================================================
void querySupportedBufferSizes (WAVEFORMATEXTENSIBLE format, ComSmartPtr<IAudioClient>& audioClient)
{
if (isLowLatencyMode (deviceMode))
{
if (auto audioClient3 = audioClient.getInterface<IAudioClient3>())
{
UINT32 defaultPeriod = 0, fundamentalPeriod = 0, minPeriod = 0, maxPeriod = 0;
if (check (audioClient3->GetSharedModeEnginePeriod ((WAVEFORMATEX*) &format,
&defaultPeriod,
&fundamentalPeriod,
&minPeriod,
&maxPeriod)))
{
minBufferSize = minPeriod;
defaultBufferSize = defaultPeriod;
lowLatencyMaxBufferSize = maxPeriod;
lowLatencyBufferSizeMultiple = fundamentalPeriod;
}
}
}
else
{
REFERENCE_TIME defaultPeriod, minPeriod;
if (! check (audioClient->GetDevicePeriod (&defaultPeriod, &minPeriod)))
return;
minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate);
defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate);
}
}
void querySupportedSampleRates (WAVEFORMATEXTENSIBLE format, ComSmartPtr<IAudioClient>& audioClient)
{
for (auto rate : { 8000, 11025, 16000, 22050, 32000,
44100, 48000, 88200, 96000, 176400,
192000, 352800, 384000, 705600, 768000 })
{
if (rates.contains (rate))
continue;
format.Format.nSamplesPerSec = (DWORD) rate;
format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8);
WAVEFORMATEX* nearestFormat = nullptr;
if (SUCCEEDED (audioClient->IsFormatSupported (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE
: AUDCLNT_SHAREMODE_SHARED,
(WAVEFORMATEX*) &format,
isExclusiveMode (deviceMode) ? nullptr
: &nearestFormat)))
{
if (nearestFormat != nullptr)
rate = nearestFormat->nSamplesPerSec;
if (! rates.contains (rate))
rates.addUsingDefaultSort (rate);
}
CoTaskMemFree (nearestFormat);
}
}
struct AudioSampleFormat
{
bool useFloat;
@@ -613,22 +720,35 @@ private:
format.SubFormat = sampleFormat.useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM;
format.dwChannelMask = newMixFormatChannelMask;
WAVEFORMATEXTENSIBLE* nearestFormat = nullptr;
WAVEFORMATEX* nearestFormat = nullptr;
HRESULT hr = clientToUse->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE
: AUDCLNT_SHAREMODE_SHARED,
HRESULT hr = clientToUse->IsFormatSupported (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE
: AUDCLNT_SHAREMODE_SHARED,
(WAVEFORMATEX*) &format,
useExclusiveMode ? nullptr : (WAVEFORMATEX**) &nearestFormat);
isExclusiveMode (deviceMode) ? nullptr
: &nearestFormat);
logFailure (hr);
if (hr == S_FALSE && format.Format.nSamplesPerSec == nearestFormat->Format.nSamplesPerSec)
auto supportsSRC = supportsSampleRateConversion (deviceMode);
if (hr == S_FALSE
&& nearestFormat != nullptr
&& (format.Format.nSamplesPerSec == nearestFormat->nSamplesPerSec
|| supportsSRC))
{
copyWavFormat (format, (const WAVEFORMATEX*) nearestFormat);
copyWavFormat (format, nearestFormat);
if (supportsSRC)
{
format.Format.nSamplesPerSec = (DWORD) newSampleRate;
format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nBlockAlign);
}
hr = S_OK;
}
CoTaskMemFree (nearestFormat);
return check (hr);
return hr == S_OK;
}
bool findSupportedFormat (IAudioClient* clientToUse, double newSampleRate,
@@ -652,50 +772,88 @@ private:
return false;
}
bool tryInitialisingWithBufferSize (int bufferSizeSamples)
DWORD getStreamFlags()
{
WAVEFORMATEXTENSIBLE format;
DWORD streamFlags = 0x40000; /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/
if (findSupportedFormat (client, sampleRate, mixFormatChannelMask, format))
if (supportsSampleRateConversion (deviceMode))
streamFlags |= (0x80000000 /*AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM*/
| 0x8000000); /*AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY*/
return streamFlags;
}
bool initialiseLowLatencyClient (int bufferSizeSamples, WAVEFORMATEXTENSIBLE format)
{
if (auto audioClient3 = client.getInterface<IAudioClient3>())
return check (audioClient3->InitializeSharedAudioStream (getStreamFlags(),
bufferSizeSamples,
(WAVEFORMATEX*) &format,
nullptr));
return false;
}
bool initialiseStandardClient (int bufferSizeSamples, WAVEFORMATEXTENSIBLE format)
{
REFERENCE_TIME defaultPeriod = 0, minPeriod = 0;
check (client->GetDevicePeriod (&defaultPeriod, &minPeriod));
if (isExclusiveMode (deviceMode) && bufferSizeSamples > 0)
defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec));
for (;;)
{
REFERENCE_TIME defaultPeriod = 0, minPeriod = 0;
GUID session;
auto hr = client->Initialize (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE
: AUDCLNT_SHAREMODE_SHARED,
getStreamFlags(),
defaultPeriod,
isExclusiveMode (deviceMode) ? defaultPeriod : 0,
(WAVEFORMATEX*) &format,
&session);
if (check (hr))
return true;
check (client->GetDevicePeriod (&defaultPeriod, &minPeriod));
// Handle the "alignment dance" : http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875(v=vs.85).aspx (see Remarks)
if (hr != MAKE_HRESULT (1, 0x889, 0x19)) // AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
break;
if (useExclusiveMode && bufferSizeSamples > 0)
defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec));
UINT32 numFrames = 0;
if (! check (client->GetBufferSize (&numFrames)))
break;
for (;;)
{
GUID session;
HRESULT hr = client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
0x40000 /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,
defaultPeriod, useExclusiveMode ? defaultPeriod : 0, (WAVEFORMATEX*) &format, &session);
// Recreate client
client = nullptr;
client = createClient();
if (check (hr))
{
actualNumChannels = format.Format.nChannels;
const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
bytesPerSample = format.Format.wBitsPerSample / 8;
bytesPerFrame = format.Format.nBlockAlign;
defaultPeriod = samplesToRefTime (numFrames, format.Format.nSamplesPerSec);
}
updateFormat (isFloat);
return true;
}
return false;
}
// Handle the "alignment dance" : http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875(v=vs.85).aspx (see Remarks)
if (hr != MAKE_HRESULT (1, 0x889, 0x19)) // AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
break;
bool tryInitialisingWithBufferSize (int bufferSizeSamples)
{
WAVEFORMATEXTENSIBLE format;
UINT32 numFrames = 0;
if (! check (client->GetBufferSize (&numFrames)))
break;
if (findSupportedFormat (client, sampleRate, mixFormatChannelMask, format))
{
auto isInitialised = isLowLatencyMode (deviceMode) ? initialiseLowLatencyClient (bufferSizeSamples, format)
: initialiseStandardClient (bufferSizeSamples, format);
if (isInitialised)
{
actualNumChannels = format.Format.nChannels;
const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
bytesPerSample = format.Format.wBitsPerSample / 8;
bytesPerFrame = format.Format.nBlockAlign;
// Recreate client
client = nullptr;
client = createClient();
updateFormat (isFloat);
defaultPeriod = samplesToRefTime (numFrames, format.Format.nSamplesPerSec);
return true;
}
}
@@ -709,8 +867,8 @@ private:
class WASAPIInputDevice : public WASAPIDeviceBase
{
public:
WASAPIInputDevice (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode)
: WASAPIDeviceBase (d, exclusiveMode)
WASAPIInputDevice (const ComSmartPtr<IMMDevice>& d, WASAPIDeviceMode mode)
: WASAPIDeviceBase (d, mode)
{
}
@@ -872,8 +1030,8 @@ private:
class WASAPIOutputDevice : public WASAPIDeviceBase
{
public:
WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode)
: WASAPIDeviceBase (d, exclusiveMode)
WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& d, WASAPIDeviceMode mode)
: WASAPIDeviceBase (d, mode)
{
}
@@ -931,7 +1089,7 @@ public:
if (numChannels <= 0)
return 0;
if (! useExclusiveMode)
if (! isExclusiveMode (deviceMode))
{
UINT32 padding = 0;
@@ -953,7 +1111,7 @@ public:
while (bufferSize > 0)
{
// This is needed in order not to drop any input data if the output device endpoint buffer was full
if ((! useExclusiveMode) && inputDevice != nullptr
if ((! isExclusiveMode (deviceMode)) && inputDevice != nullptr
&& WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
inputDevice->handleDeviceBuffer();
@@ -968,7 +1126,7 @@ public:
break;
}
if (useExclusiveMode && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT)
if (isExclusiveMode (deviceMode) && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT)
break;
uint8* outputData = nullptr;
@@ -1002,12 +1160,12 @@ public:
const String& typeName,
const String& outputDeviceID,
const String& inputDeviceID,
bool exclusiveMode)
WASAPIDeviceMode mode)
: AudioIODevice (deviceName, typeName),
Thread ("JUCE WASAPI"),
outputDeviceId (outputDeviceID),
inputDeviceId (inputDeviceID),
useExclusiveMode (exclusiveMode)
deviceMode (mode)
{
}
@@ -1026,21 +1184,48 @@ public:
{
jassert (inputDevice != nullptr || outputDevice != nullptr);
sampleRates.clear();
if (inputDevice != nullptr && outputDevice != nullptr)
{
defaultSampleRate = jmin (inputDevice->defaultSampleRate, outputDevice->defaultSampleRate);
minBufferSize = jmin (inputDevice->minBufferSize, outputDevice->minBufferSize);
minBufferSize = jmax (inputDevice->minBufferSize, outputDevice->minBufferSize);
defaultBufferSize = jmax (inputDevice->defaultBufferSize, outputDevice->defaultBufferSize);
sampleRates = inputDevice->rates;
sampleRates.removeValuesNotIn (outputDevice->rates);
if (isLowLatencyMode (deviceMode))
{
lowLatencyMaxBufferSize = jmin (inputDevice->lowLatencyMaxBufferSize, outputDevice->lowLatencyMaxBufferSize);
lowLatencyBufferSizeMultiple = jmax (inputDevice->lowLatencyBufferSizeMultiple, outputDevice->lowLatencyBufferSizeMultiple);
}
sampleRates.addArray (inputDevice->rates);
if (supportsSampleRateConversion (deviceMode))
{
for (auto r : outputDevice->rates)
if (! sampleRates.contains (r))
sampleRates.addUsingDefaultSort (r);
}
else
{
sampleRates.removeValuesNotIn (outputDevice->rates);
}
}
else
{
WASAPIDeviceBase* d = inputDevice != nullptr ? static_cast<WASAPIDeviceBase*> (inputDevice.get())
: static_cast<WASAPIDeviceBase*> (outputDevice.get());
auto* d = inputDevice != nullptr ? static_cast<WASAPIDeviceBase*> (inputDevice.get())
: static_cast<WASAPIDeviceBase*> (outputDevice.get());
defaultSampleRate = d->defaultSampleRate;
minBufferSize = d->minBufferSize;
defaultBufferSize = d->defaultBufferSize;
if (isLowLatencyMode (deviceMode))
{
lowLatencyMaxBufferSize = d->lowLatencyMaxBufferSize;
lowLatencyBufferSizeMultiple = d->lowLatencyBufferSizeMultiple;
}
sampleRates = d->rates;
}
@@ -1050,13 +1235,28 @@ public:
if (minBufferSize != defaultBufferSize)
bufferSizes.addUsingDefaultSort (minBufferSize);
int n = 64;
for (int i = 0; i < 40; ++i)
if (isLowLatencyMode (deviceMode))
{
if (n >= minBufferSize && n <= 2048 && ! bufferSizes.contains (n))
bufferSizes.addUsingDefaultSort (n);
auto size = minBufferSize;
while (size < lowLatencyMaxBufferSize)
{
size += lowLatencyBufferSizeMultiple;
n += (n < 512) ? 32 : (n < 1024 ? 64 : 128);
if (! bufferSizes.contains (size))
bufferSizes.addUsingDefaultSort (size);
}
}
else
{
int n = 64;
for (int i = 0; i < 40; ++i)
{
if (n >= minBufferSize && n <= 2048 && ! bufferSizes.contains (n))
bufferSizes.addUsingDefaultSort (n);
n += (n < 512) ? 32 : (n < 1024 ? 64 : 128);
}
}
return true;
@@ -1131,7 +1331,7 @@ public:
return lastError;
}
if (useExclusiveMode)
if (isExclusiveMode (deviceMode))
{
// This is to make sure that the callback uses actualBufferSize in case of exclusive mode
if (inputDevice != nullptr && outputDevice != nullptr && inputDevice->actualBufferSize != outputDevice->actualBufferSize)
@@ -1297,7 +1497,7 @@ public:
}
else
{
if (useExclusiveMode && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
if (isExclusiveMode (deviceMode) && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
inputDevice->handleDeviceBuffer();
}
@@ -1347,9 +1547,10 @@ private:
// Device stats...
std::unique_ptr<WASAPIInputDevice> inputDevice;
std::unique_ptr<WASAPIOutputDevice> outputDevice;
const bool useExclusiveMode;
WASAPIDeviceMode deviceMode;
double defaultSampleRate = 0;
int minBufferSize = 0, defaultBufferSize = 0;
int lowLatencyMaxBufferSize = 0, lowLatencyBufferSizeMultiple = 0;
int latencyIn = 0, latencyOut = 0;
Array<double> sampleRates;
Array<int> bufferSizes;
@@ -1399,9 +1600,9 @@ private:
auto flow = getDataFlow (device);
if (deviceId == inputDeviceId && flow == eCapture)
inputDevice.reset (new WASAPIInputDevice (device, useExclusiveMode));
inputDevice.reset (new WASAPIInputDevice (device, deviceMode));
else if (deviceId == outputDeviceId && flow == eRender)
outputDevice.reset (new WASAPIOutputDevice (device, useExclusiveMode));
outputDevice.reset (new WASAPIOutputDevice (device, deviceMode));
}
return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk()))
@@ -1458,10 +1659,10 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType,
private DeviceChangeDetector
{
public:
WASAPIAudioIODeviceType (bool exclusive)
: AudioIODeviceType (exclusive ? "Windows Audio (Exclusive Mode)" : "Windows Audio"),
WASAPIAudioIODeviceType (WASAPIDeviceMode mode)
: AudioIODeviceType (getDeviceTypename (mode)),
DeviceChangeDetector (L"Windows Audio"),
exclusiveMode (exclusive)
deviceMode (mode)
{
}
@@ -1529,7 +1730,7 @@ public:
getTypeName(),
outputDeviceIds [outputIndex],
inputDeviceIds [inputIndex],
exclusiveMode));
deviceMode));
if (! device->initialise())
device = nullptr;
@@ -1543,7 +1744,7 @@ public:
StringArray inputDeviceNames, inputDeviceIds;
private:
const bool exclusiveMode;
WASAPIDeviceMode deviceMode;
bool hasScanned = false;
ComSmartPtr<IMMDeviceEnumerator> enumerator;
@@ -1698,6 +1899,17 @@ private:
callDeviceChangeListeners();
}
//==============================================================================
static String getDeviceTypename (WASAPIDeviceMode mode)
{
if (mode == WASAPIDeviceMode::shared) return "Windows Audio";
if (mode == WASAPIDeviceMode::sharedLowLatency) return "Windows Audio (Low Latency Mode)";
if (mode == WASAPIDeviceMode::exclusive) return "Windows Audio (Exclusive Mode)";
jassertfalse;
return {};
}
//==============================================================================
JUCE_DECLARE_WEAK_REFERENCEABLE (WASAPIAudioIODeviceType)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType)
@@ -1756,19 +1968,6 @@ struct MMDeviceMasterVolume
}
//==============================================================================
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode)
{
#if ! JUCE_WASAPI_EXCLUSIVE
if (exclusiveMode)
return nullptr;
#endif
return SystemStats::getOperatingSystemType() >= SystemStats::WinVista
? new WasapiClasses::WASAPIAudioIODeviceType (exclusiveMode)
: nullptr;
}
//==============================================================================
#define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1
float JUCE_CALLTYPE SystemAudioVolume::getGain() { return WasapiClasses::MMDeviceMasterVolume().getGain(); }


+ 10
- 11
source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm View File

@@ -39,8 +39,7 @@
#ifndef JUCE_SUPPORTS_AUv3
#if __OBJC2__ \
&& ((defined (MAC_OS_X_VERSION_10_11) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)) \
|| (defined (__IPHONE_9_0) && (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0)))
&& (JUCE_IOS || (defined (MAC_OS_X_VERSION_10_11) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)))
#define JUCE_SUPPORTS_AUv3 1
#else
#define JUCE_SUPPORTS_AUv3 0
@@ -79,7 +78,7 @@ namespace AudioUnitFormatHelpers
static ThreadLocalValue<int> insideCallback;
#endif
String osTypeToString (OSType type) noexcept
static String osTypeToString (OSType type) noexcept
{
const juce_wchar s[4] = { (juce_wchar) ((type >> 24) & 0xff),
(juce_wchar) ((type >> 16) & 0xff),
@@ -88,7 +87,7 @@ namespace AudioUnitFormatHelpers
return String (s, 4);
}
OSType stringToOSType (String s)
static OSType stringToOSType (String s)
{
if (s.trim().length() >= 4) // (to avoid trimming leading spaces)
s = s.trim();
@@ -103,7 +102,7 @@ namespace AudioUnitFormatHelpers
static const char* auIdentifierPrefix = "AudioUnit:";
String createPluginIdentifier (const AudioComponentDescription& desc)
static String createPluginIdentifier (const AudioComponentDescription& desc)
{
String s (auIdentifierPrefix);
@@ -128,7 +127,7 @@ namespace AudioUnitFormatHelpers
return s;
}
void getNameAndManufacturer (AudioComponent comp, String& name, String& manufacturer)
static void getNameAndManufacturer (AudioComponent comp, String& name, String& manufacturer)
{
CFStringRef cfName;
if (AudioComponentCopyName (comp, &cfName) == noErr)
@@ -147,8 +146,8 @@ namespace AudioUnitFormatHelpers
name = "<Unknown>";
}
bool getComponentDescFromIdentifier (const String& fileOrIdentifier, AudioComponentDescription& desc,
String& name, String& version, String& manufacturer)
static bool getComponentDescFromIdentifier (const String& fileOrIdentifier, AudioComponentDescription& desc,
String& name, String& version, String& manufacturer)
{
if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix))
{
@@ -193,8 +192,8 @@ namespace AudioUnitFormatHelpers
return false;
}
bool getComponentDescFromFile (const String& fileOrIdentifier, AudioComponentDescription& desc,
String& name, String& version, String& manufacturer)
static bool getComponentDescFromFile (const String& fileOrIdentifier, AudioComponentDescription& desc,
String& name, String& version, String& manufacturer)
{
zerostruct (desc);
@@ -296,7 +295,7 @@ namespace AudioUnitFormatHelpers
#endif
}
const char* getCategory (OSType type) noexcept
static const char* getCategory (OSType type) noexcept
{
switch (type)
{


+ 87
- 20
source/modules/juce_audio_processors/format_types/juce_VST3Common.h View File

@@ -401,8 +401,8 @@ private:
class MidiEventList : public Steinberg::Vst::IEventList
{
public:
MidiEventList() {}
virtual ~MidiEventList() {}
MidiEventList() = default;
virtual ~MidiEventList() = default;
JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_METHODS
@@ -455,9 +455,43 @@ public:
}
}
static void hostToPluginEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer,
Steinberg::Vst::IParameterChanges* parameterChanges,
Steinberg::Vst::IMidiMapping* midiMapping)
{
toEventList (result,
midiBuffer,
parameterChanges,
midiMapping,
EventConversionKind::hostToPlugin);
}
static void pluginToHostEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer)
{
toEventList (result,
midiBuffer,
nullptr,
nullptr,
EventConversionKind::pluginToHost);
}
private:
enum class EventConversionKind
{
// Hosted plugins don't expect to receive LegacyMIDICCEvents messages from the host,
// so if we're converting midi from the host to an eventlist, this mode will avoid
// converting to Legacy events where possible.
hostToPlugin,
// If plugins generate MIDI internally, then where possible we should preserve
// these messages as LegacyMIDICCOut events.
pluginToHost
};
static void toEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer,
Steinberg::Vst::IParameterChanges* parameterChanges = nullptr,
Steinberg::Vst::IMidiMapping* midiMapping = nullptr)
Steinberg::Vst::IParameterChanges* parameterChanges,
Steinberg::Vst::IMidiMapping* midiMapping,
EventConversionKind kind)
{
enum { maxNumEvents = 2048 }; // Steinberg's Host Checker states that no more than 2048 events are allowed at once
int numEvents = 0;
@@ -491,7 +525,7 @@ public:
}
}
auto maybeEvent = createVstEvent (msg, metadata.data);
auto maybeEvent = createVstEvent (msg, metadata.data, kind);
if (! maybeEvent.isValid)
continue;
@@ -503,7 +537,6 @@ public:
}
}
private:
Array<Steinberg::Vst::Event, CriticalSection> events;
Atomic<int> refCount;
@@ -555,10 +588,21 @@ private:
{
Steinberg::Vst::Event e{};
e.type = Steinberg::Vst::Event::kLegacyMIDICCOutEvent;
e.midiCCOut.channel = int8 (createSafeChannel (channel));
e.midiCCOut.channel = Steinberg::int8 (createSafeChannel (channel));
e.midiCCOut.controlNumber = uint8 (jlimit (0, 255, controlNumber));
e.midiCCOut.value = int8 (createSafeNote (value));
e.midiCCOut.value2 = int8 (createSafeNote (value2));
e.midiCCOut.value = Steinberg::int8 (createSafeNote (value));
e.midiCCOut.value2 = Steinberg::int8 (createSafeNote (value2));
return e;
}
static Steinberg::Vst::Event createPolyPressureEvent (const MidiMessage& msg)
{
Steinberg::Vst::Event e{};
e.type = Steinberg::Vst::Event::kPolyPressureEvent;
e.polyPressure.channel = createSafeChannel (msg.getChannel());
e.polyPressure.pitch = createSafeNote (msg.getNoteNumber());
e.polyPressure.pressure = normaliseMidiValue (msg.getAfterTouchValue());
e.polyPressure.noteId = -1;
return e;
}
@@ -610,14 +654,15 @@ private:
struct BasicOptional final
{
BasicOptional() noexcept = default;
BasicOptional (const Item& i) noexcept : item ( i ), isValid ( true ) {}
BasicOptional (const Item& i) noexcept : item { i }, isValid { true } {}
Item item;
bool isValid{};
};
static BasicOptional<Steinberg::Vst::Event> createVstEvent (const MidiMessage& msg,
const uint8* midiEventData) noexcept
const uint8* midiEventData,
EventConversionKind kind) noexcept
{
if (msg.isNoteOn())
return createNoteOnEvent (msg);
@@ -643,11 +688,20 @@ private:
if (msg.isQuarterFrame())
return createCtrlQuarterFrameEvent (msg);
// VST3 gives us two ways to communicate poly pressure changes.
// There's a dedicated PolyPressureEvent, and also a LegacyMIDICCOutEvent with a
// `controlNumber` of `kCtrlPolyPressure`. We're sending the LegacyMIDI version.
if (msg.isAftertouch())
return createCtrlPolyPressureEvent (msg);
{
switch (kind)
{
case EventConversionKind::hostToPlugin:
return createPolyPressureEvent (msg);
case EventConversionKind::pluginToHost:
return createCtrlPolyPressureEvent (msg);
}
jassertfalse;
return {};
}
return {};
}
@@ -738,13 +792,26 @@ private:
static bool toVst3ControlEvent (const MidiMessage& msg, Vst3MidiControlEvent& result)
{
result.controllerNumber = -1;
if (msg.isController())
{
result = { (Steinberg::Vst::CtrlNumber) msg.getControllerNumber(), msg.getControllerValue() / 127.0};
return true;
}
if (msg.isController()) result = { (Steinberg::Vst::CtrlNumber) msg.getControllerNumber(), msg.getControllerValue() / 127.0};
else if (msg.isPitchWheel()) result = { Steinberg::Vst::kPitchBend, msg.getPitchWheelValue() / 16383.0};
else if (msg.isAftertouch()) result = { Steinberg::Vst::kAfterTouch, msg.getAfterTouchValue() / 127.0};
if (msg.isPitchWheel())
{
result = { Steinberg::Vst::kPitchBend, msg.getPitchWheelValue() / 16383.0};
return true;
}
if (msg.isChannelPressure())
{
result = { Steinberg::Vst::kAfterTouch, msg.getChannelPressureValue() / 127.0};
return true;
}
return (result.controllerNumber != -1);
result.controllerNumber = -1;
return false;
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiEventList)


+ 3
- 1
source/modules/juce_audio_processors/format_types/juce_VST3Headers.h View File

@@ -55,7 +55,9 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnon-virtual-dtor",
"-Wformat",
"-Wpedantic",
"-Wextra",
"-Wclass-memaccess")
"-Wclass-memaccess",
"-Wmissing-prototypes",
"-Wtype-limits")
#undef DEVELOPMENT
#define DEVELOPMENT 0 // This avoids a Clang warning in Steinberg code about unused values


+ 169
- 155
source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -818,65 +818,43 @@ private:
//==============================================================================
struct DLLHandle
{
DLLHandle (const String& modulePath)
DLLHandle (const File& fileToOpen)
: dllFile (fileToOpen)
{
if (modulePath.trim().isNotEmpty())
open (modulePath);
open();
}
~DLLHandle()
{
typedef bool (PLUGIN_API *ExitModuleFn) ();
#if JUCE_WINDOWS || JUCE_LINUX
releaseFactory();
#if JUCE_WINDOWS
if (auto exitFn = (ExitModuleFn) getFunction ("ExitDll"))
#else
if (auto exitFn = (ExitModuleFn) getFunction ("ModuleExit"))
#endif
exitFn();
library.close();
#elif JUCE_MAC
#if JUCE_MAC
if (bundleRef != nullptr)
#endif
{
releaseFactory();
if (factory != nullptr)
factory->release();
if (auto exitFn = (ExitModuleFn) getFunction ("bundleExit"))
using ExitModuleFn = bool (PLUGIN_API*) ();
if (auto* exitFn = (ExitModuleFn) getFunction (exitFnName))
exitFn();
CFBundleUnloadExecutable (bundleRef);
#if JUCE_WINDOWS || JUCE_LINUX
library.close();
#elif JUCE_MAC
CFRelease (bundleRef);
bundleRef = nullptr;
#endif
}
#endif
}
void open (const PluginDescription& description)
{
#if JUCE_WINDOWS || JUCE_LINUX
jassert (description.fileOrIdentifier.isNotEmpty());
jassert (File (description.fileOrIdentifier).existsAsFile());
library.open (description.fileOrIdentifier);
#elif JUCE_MAC
open (description.fileOrIdentifier);
#endif
}
/** @note The factory should begin with a refCount of 1,
so don't increment the reference count
(ie: don't use a ComSmartPtr in here)!
Its lifetime will be handled by this DllHandle,
when such will be destroyed.
@see releaseFactory
//==============================================================================
/** The factory should begin with a refCount of 1, so don't increment the reference count
(ie: don't use a ComSmartPtr in here)! Its lifetime will be handled by this DLLHandle.
*/
IPluginFactory* JUCE_CALLTYPE getPluginFactory()
{
if (factory == nullptr)
if (auto proc = (GetFactoryProc) getFunction ("GetPluginFactory"))
if (auto* proc = (GetFactoryProc) getFunction (factoryFnName))
factory = proc();
// The plugin NEEDS to provide a factory to be able to be called a VST3!
@@ -894,38 +872,56 @@ struct DLLHandle
if (bundleRef == nullptr)
return nullptr;
CFStringRef name = String (functionName).toCFString();
void* fn = CFBundleGetFunctionPointerForName (bundleRef, name);
CFRelease (name);
return fn;
ScopedCFString name (functionName);
return CFBundleGetFunctionPointerForName (bundleRef, name.cfString);
#endif
}
File getFile() const noexcept { return dllFile; }
private:
File dllFile;
IPluginFactory* factory = nullptr;
void releaseFactory()
{
if (factory != nullptr)
factory->release();
}
static constexpr const char* factoryFnName = "GetPluginFactory";
#if JUCE_WINDOWS
static constexpr const char* entryFnName = "InitDll";
static constexpr const char* exitFnName = "ExitDll";
using EntryProc = bool (PLUGIN_API*) ();
#elif JUCE_LINUX
static constexpr const char* entryFnName = "ModuleEntry";
static constexpr const char* exitFnName = "ModuleExit";
using EntryProc = bool (PLUGIN_API*) (void*);
#elif JUCE_MAC
static constexpr const char* entryFnName = "bundleEntry";
static constexpr const char* exitFnName = "bundleExit";
using EntryProc = bool (*) (CFBundleRef);
#endif
//==============================================================================
#if JUCE_WINDOWS || JUCE_LINUX
DynamicLibrary library;
bool open (const String& filePath)
bool open()
{
if (library.open (filePath))
if (library.open (dllFile.getFullPathName()))
{
typedef bool (PLUGIN_API *InitModuleProc) ();
if (auto proc = (InitModuleProc) getFunction ("InitDll"))
if (auto* proc = (EntryProc) getFunction (entryFnName))
{
#if JUCE_WINDOWS
if (proc())
#else
if (proc (library.getNativeHandle()))
#endif
return true;
}
else
{
// this is required for some plug-ins which don't export the dll entry point function
return true;
}
@@ -937,12 +933,14 @@ private:
#elif JUCE_MAC
CFBundleRef bundleRef;
bool open (const String& filePath)
bool open()
{
const File file (filePath);
const char* const utf8 = file.getFullPathName().toRawUTF8();
auto* utf8 = dllFile.getFullPathName().toRawUTF8();
if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (nullptr, (const UInt8*) utf8, (CFIndex) std::strlen (utf8), file.isDirectory()))
if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (nullptr,
(const UInt8*) utf8,
(CFIndex) std::strlen (utf8),
dllFile.isDirectory()))
{
bundleRef = CFBundleCreate (kCFAllocatorDefault, url);
CFRelease (url);
@@ -953,17 +951,11 @@ private:
if (CFBundleLoadExecutableAndReturnError (bundleRef, &error))
{
using BundleEntryProc = bool (*)(CFBundleRef);
if (auto proc = (BundleEntryProc) getFunction ("bundleEntry"))
if (auto* proc = (EntryProc) getFunction (entryFnName))
{
if (proc (bundleRef))
return true;
}
else
{
return true;
}
}
if (error != nullptr)
@@ -984,127 +976,122 @@ private:
return false;
}
#elif JUCE_LINUX
DynamicLibrary library;
#endif
String getMachineName()
{
struct utsname unameData;
auto res = uname (&unameData);
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DLLHandle)
};
if (res != 0)
return {};
struct DLLHandleCache : public DeletedAtShutdown
{
DLLHandleCache() = default;
~DLLHandleCache() override { clearSingletonInstance(); }
return unameData.machine;
}
JUCE_DECLARE_SINGLETON (DLLHandleCache, false)
bool open (const String& bundlePath)
DLLHandle& findOrCreateHandle (const String& modulePath)
{
File file (bundlePath);
if (! file.exists() || ! file.isDirectory())
return false;
#if JUCE_LINUX
File file (getDLLFileFromBundle (modulePath));
#else
File file (modulePath);
#endif
auto pluginName = file.getFileNameWithoutExtension();
auto it = std::find_if (openHandles.begin(), openHandles.end(),
[&] (const std::unique_ptr<DLLHandle>& handle)
{
return file == handle->getFile();
});
file = file.getChildFile ("Contents")
.getChildFile (getMachineName() + "-linux")
.getChildFile (pluginName + ".so");
if (it != openHandles.end())
return *it->get();
if (! file.exists())
return false;
openHandles.push_back (std::make_unique<DLLHandle> (file));
return *openHandles.back().get();
}
if (library.open (file.getFullPathName()))
private:
#if JUCE_LINUX
File getDLLFileFromBundle (const String& bundlePath) const
{
auto machineName = []() -> String
{
typedef bool (PLUGIN_API *InitModuleProc) (void*);
struct utsname unameData;
auto res = uname (&unameData);
if (auto* proc = (InitModuleProc) getFunction ("ModuleEntry"))
{
if (proc (library.getNativeHandle()))
return true;
}
else
{
return true;
}
if (res != 0)
return {};
library.close();
}
return unameData.machine;
}();
return false;
File file (bundlePath);
return file.getChildFile ("Contents")
.getChildFile (machineName + "-linux")
.getChildFile (file.getFileNameWithoutExtension() + ".so");
}
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DLLHandle)
std::vector<std::unique_ptr<DLLHandle>> openHandles;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DLLHandleCache)
};
JUCE_IMPLEMENT_SINGLETON (DLLHandleCache)
//==============================================================================
struct VST3ModuleHandle : public ReferenceCountedObject
{
explicit VST3ModuleHandle (const File& pluginFile) : file (pluginFile)
explicit VST3ModuleHandle (const File& pluginFile, const PluginDescription& pluginDesc)
: file (pluginFile)
{
getActiveModules().add (this);
if (open (pluginDesc))
{
isOpen = true;
getActiveModules().add (this);
}
}
~VST3ModuleHandle()
{
getActiveModules().removeFirstMatchingValue (this);
}
/**
Since there is no apparent indication if a VST3 plugin is a shell or not,
we're stuck iterating through a VST3's factory, creating a description
for every housed plugin.
*/
static bool getAllDescriptionsForFile (OwnedArray<PluginDescription>& results,
const String& fileOrIdentifier)
{
DLLHandle tempModule (fileOrIdentifier);
ComSmartPtr<IPluginFactory> pluginFactory (tempModule.getPluginFactory());
if (pluginFactory != nullptr)
{
ComSmartPtr<VST3HostContext> host (new VST3HostContext());
DescriptionLister lister (host, pluginFactory);
auto result = lister.findDescriptionsAndPerform (File (fileOrIdentifier));
results.addCopiesOf (lister.list);
return result.wasOk();
}
jassertfalse;
return false;
if (isOpen)
getActiveModules().removeFirstMatchingValue (this);
}
//==============================================================================
using Ptr = ReferenceCountedObjectPtr<VST3ModuleHandle>;
static VST3ModuleHandle::Ptr findOrCreateModule (const File& file, const PluginDescription& description)
static VST3ModuleHandle::Ptr findOrCreateModule (const File& file,
const PluginDescription& description)
{
for (auto* module : getActiveModules())
{
// VST3s are basically shells, you must therefore check their name along with their file:
if (module->file == file && module->name == description.name)
return module;
}
VST3ModuleHandle::Ptr m (new VST3ModuleHandle (file));
VST3ModuleHandle::Ptr modulePtr (new VST3ModuleHandle (file, description));
if (! m->open (file, description))
m = nullptr;
if (! modulePtr->isOpen)
modulePtr = nullptr;
return m;
return modulePtr;
}
//==============================================================================
IPluginFactory* getPluginFactory() { return dllHandle->getPluginFactory(); }
IPluginFactory* getPluginFactory()
{
return DLLHandleCache::getInstance()->findOrCreateHandle (file.getFullPathName()).getPluginFactory();
}
File file;
String name;
File getFile() const noexcept { return file; }
String getName() const noexcept { return name; }
private:
std::unique_ptr<DLLHandle> dllHandle;
//==============================================================================
static Array<VST3ModuleHandle*>& getActiveModules()
{
@@ -1113,11 +1100,10 @@ private:
}
//==============================================================================
bool open (const File& f, const PluginDescription& description)
bool open (const PluginDescription& description)
{
dllHandle.reset (new DLLHandle (f.getFullPathName()));
ComSmartPtr<IPluginFactory> pluginFactory (dllHandle->getPluginFactory());
ComSmartPtr<IPluginFactory> pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (file.getFullPathName())
.getPluginFactory());
if (pluginFactory != nullptr)
{
@@ -1132,7 +1118,7 @@ private:
continue;
if (toString (info.name).trim() == description.name
&& getHashForTUID (info.cid) == description.uid)
&& getHashForTUID (info.cid) == description.uid)
{
name = description.name;
return true;
@@ -1143,6 +1129,11 @@ private:
return false;
}
File file;
String name;
bool isOpen = false;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3ModuleHandle)
};
@@ -1618,7 +1609,7 @@ struct VST3ComponentHolder
PFactoryInfo factoryInfo;
factory->getFactoryInfo (&factoryInfo);
auto classIdx = getClassIndex (module->name);
auto classIdx = getClassIndex (module->getName());
if (classIdx >= 0)
{
@@ -1667,8 +1658,8 @@ struct VST3ComponentHolder
if (component->getBusInfo (Vst::kAudio, Vst::kOutput, i, bus) == kResultOk)
totalNumOutputChannels += ((bus.flags & Vst::BusInfo::kDefaultActive) != 0 ? bus.channelCount : 0);
createPluginDescription (description, module->file,
factoryInfo.vendor, module->name,
createPluginDescription (description, module->getFile(),
factoryInfo.vendor, module->getName(),
info, info2.get(), infoW.get(),
totalNumInputChannels,
totalNumOutputChannels);
@@ -1695,7 +1686,7 @@ struct VST3ComponentHolder
factory = ComSmartPtr<IPluginFactory> (module->getPluginFactory());
int classIdx;
if ((classIdx = getClassIndex (module->name)) < 0)
if ((classIdx = getClassIndex (module->getName())) < 0)
return false;
PClassInfo info;
@@ -1991,7 +1982,7 @@ public:
const String getName() const override
{
auto& module = holder->module;
return module != nullptr ? module->name : String();
return module != nullptr ? module->getName() : String();
}
void repopulateArrangements (Array<Vst::SpeakerArrangement>& inputArrangements, Array<Vst::SpeakerArrangement>& outputArrangements) const
@@ -2979,9 +2970,10 @@ private:
midiOutputs->clear();
if (acceptsMidi())
MidiEventList::toEventList (*midiInputs, midiBuffer,
destination.inputParameterChanges,
midiMapping);
MidiEventList::hostToPluginEventList (*midiInputs,
midiBuffer,
destination.inputParameterChanges,
midiMapping);
destination.inputEvents = midiInputs;
destination.outputEvents = midiOutputs;
@@ -3315,7 +3307,29 @@ bool VST3PluginFormat::setStateFromVSTPresetFile (AudioPluginInstance* api, cons
void VST3PluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& results, const String& fileOrIdentifier)
{
if (fileMightContainThisPluginType (fileOrIdentifier))
VST3ModuleHandle::getAllDescriptionsForFile (results, fileOrIdentifier);
{
/**
Since there is no apparent indication if a VST3 plugin is a shell or not,
we're stuck iterating through a VST3's factory, creating a description
for every housed plugin.
*/
ComSmartPtr<IPluginFactory> pluginFactory (DLLHandleCache::getInstance()->findOrCreateHandle (fileOrIdentifier)
.getPluginFactory());
if (pluginFactory != nullptr)
{
ComSmartPtr<VST3HostContext> host (new VST3HostContext());
DescriptionLister lister (host, pluginFactory);
lister.findDescriptionsAndPerform (File (fileOrIdentifier));
results.addCopiesOf (lister.list);
}
else
{
jassertfalse;
}
}
}
void VST3PluginFormat::createPluginInstance (const PluginDescription& description,


+ 0
- 7
source/modules/juce_audio_processors/format_types/juce_VSTCommon.h View File

@@ -26,13 +26,6 @@
namespace juce
{
//==============================================================================
#if JUCE_BIG_ENDIAN
#define JUCE_MULTICHAR_CONSTANT(a, b, c, d) (a | (((uint32) b) << 8) | (((uint32) c) << 16) | (((uint32) d) << 24))
#else
#define JUCE_MULTICHAR_CONSTANT(a, b, c, d) (d | (((uint32) c) << 8) | (((uint32) b) << 16) | (((uint32) a) << 24))
#endif
//==============================================================================
/** Structure for VST speaker mappings


+ 4
- 1
source/modules/juce_audio_processors/format_types/juce_VSTInterface.h View File

@@ -24,6 +24,7 @@
==============================================================================
*/
#ifndef JUCE_VSTINTERFACE_H_INCLUDED
#define JUCE_VSTINTERFACE_H_INCLUDED
using namespace juce;
@@ -505,7 +506,7 @@ enum PresonusExtensionConstants
@tags{Audio}
*/
struct vst2FxBank
struct fxBank
{
int32 magic1;
int32 size;
@@ -527,3 +528,5 @@ struct vst2FxBank
#else
#pragma pack(pop)
#endif
#endif // JUCE_VSTINTERFACE_H_INCLUDED

+ 9
- 10
source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp View File

@@ -173,7 +173,7 @@ namespace
#elif JUCE_LINUX || JUCE_IOS || JUCE_ANDROID
timeval micro;
gettimeofday (&micro, nullptr);
return micro.tv_usec * 1000.0;
return (double) micro.tv_usec * 1000.0;
#elif JUCE_MAC
UnsignedWide micro;
Microseconds (&micro);
@@ -441,8 +441,8 @@ private:
}
else
{
entry->range.low = curEntry / (float) numEntries;
entry->range.high = (curEntry + 1) / (float) numEntries;
entry->range.low = (float) curEntry / (float) numEntries;
entry->range.high = (float) (curEntry + 1) / (float) numEntries;
entry->range.inclusiveLow = true;
entry->range.inclusiveHigh = (curEntry == numEntries - 1);
@@ -2872,8 +2872,8 @@ public:
{
X11Symbols::getInstance()->xMoveResizeWindow (display, pluginWindow,
pos.getX(), pos.getY(),
static_cast<unsigned int> (roundToInt (getWidth() * nativeScaleFactor)),
static_cast<unsigned int> (roundToInt (getHeight() * nativeScaleFactor)));
static_cast<unsigned int> (roundToInt ((float) getWidth() * nativeScaleFactor)),
static_cast<unsigned int> (roundToInt ((float) getHeight() * nativeScaleFactor)));
X11Symbols::getInstance()->xMapRaised (display, pluginWindow);
X11Symbols::getInstance()->xFlush (display);
@@ -2939,8 +2939,8 @@ public:
if (pluginRespondsToDPIChanges)
dispatch (Vst2::plugInOpcodeManufacturerSpecific,
JUCE_MULTICHAR_CONSTANT ('P', 'r', 'e', 'S'),
JUCE_MULTICHAR_CONSTANT ('A', 'e', 'C', 's'),
(int) ByteOrder::bigEndianInt ("PreS"),
(int) ByteOrder::bigEndianInt ("AeCs"),
nullptr, nativeScaleFactor);
}
#endif
@@ -3164,8 +3164,8 @@ private:
X11Symbols::getInstance()->xMapRaised (display, pluginWindow);
#endif
w = roundToInt (w / nativeScaleFactor);
h = roundToInt (h / nativeScaleFactor);
w = roundToInt ((float) w / nativeScaleFactor);
h = roundToInt ((float) h / nativeScaleFactor);
// double-check it's not too tiny
w = jmax (w, 32);
@@ -3631,7 +3631,6 @@ FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch()
FileSearchPath paths;
paths.add (WindowsRegistry::getValue ("HKEY_LOCAL_MACHINE\\Software\\VST\\VSTPluginsPath"));
paths.addIfNotAlreadyThere (programFiles + "\\Steinberg\\VstPlugins");
paths.removeNonExistentPaths();
paths.addIfNotAlreadyThere (programFiles + "\\VstPlugins");
paths.removeRedundantPaths();
return paths;


+ 1
- 1
source/modules/juce_audio_processors/juce_audio_processors.h View File

@@ -35,7 +35,7 @@
ID: juce_audio_processors
vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE audio processor classes
description: Classes for loading and playing VST, AU, LADSPA, or internally-generated audio processors.
website: http://www.juce.com/juce


+ 0
- 31
source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp View File

@@ -1567,35 +1567,4 @@ void AudioProcessorParameter::removeListener (AudioProcessorParameter::Listener*
listeners.removeFirstMatchingValue (listenerToRemove);
}
//==============================================================================
bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept
{
return timeInSamples == other.timeInSamples
&& ppqPosition == other.ppqPosition
&& editOriginTime == other.editOriginTime
&& ppqPositionOfLastBarStart == other.ppqPositionOfLastBarStart
&& frameRate == other.frameRate
&& isPlaying == other.isPlaying
&& isRecording == other.isRecording
&& bpm == other.bpm
&& timeSigNumerator == other.timeSigNumerator
&& timeSigDenominator == other.timeSigDenominator
&& ppqLoopStart == other.ppqLoopStart
&& ppqLoopEnd == other.ppqLoopEnd
&& isLooping == other.isLooping;
}
bool AudioPlayHead::CurrentPositionInfo::operator!= (const CurrentPositionInfo& other) const noexcept
{
return ! operator== (other);
}
void AudioPlayHead::CurrentPositionInfo::resetToDefault()
{
zerostruct (*this);
timeSigNumerator = 4;
timeSigDenominator = 4;
bpm = 120;
}
} // namespace juce

+ 9
- 2
source/modules/juce_audio_processors/utilities/juce_ParameterAttachments.cpp View File

@@ -191,6 +191,7 @@ ComboBoxParameterAttachment::ComboBoxParameterAttachment (RangedAudioParameter&
ComboBox& c,
UndoManager* um)
: comboBox (c),
storedParameter (param),
attachment (param, [this] (float f) { setValue (f); }, um)
{
sendInitialUpdate();
@@ -209,7 +210,8 @@ void ComboBoxParameterAttachment::sendInitialUpdate()
void ComboBoxParameterAttachment::setValue (float newValue)
{
const auto index = roundToInt (newValue);
const auto normValue = storedParameter.convertTo0to1 (newValue);
const auto index = roundToInt (normValue * (float) (comboBox.getNumItems() - 1));
if (index == comboBox.getSelectedItemIndex())
return;
@@ -223,7 +225,12 @@ void ComboBoxParameterAttachment::comboBoxChanged (ComboBox*)
if (ignoreCallbacks)
return;
attachment.setValueAsCompleteGesture ((float) comboBox.getSelectedItemIndex());
const auto numItems = comboBox.getNumItems();
const auto selected = (float) comboBox.getSelectedItemIndex();
const auto newValue = numItems > 1 ? selected / (float) (numItems - 1)
: 0.0f;
attachment.setValueAsCompleteGesture (storedParameter.convertFrom0to1 (newValue));
}
//==============================================================================


+ 1
- 0
source/modules/juce_audio_processors/utilities/juce_ParameterAttachments.h View File

@@ -203,6 +203,7 @@ private:
void comboBoxChanged (ComboBox*) override;
ComboBox& comboBox;
RangedAudioParameter& storedParameter;
ParameterAttachment attachment;
bool ignoreCallbacks = false;
};


+ 4
- 4
source/modules/juce_core/containers/juce_ArrayBase.cpp View File

@@ -87,14 +87,14 @@ namespace ArrayBaseTestsHelpers
};
}
bool operator== (const ArrayBaseTestsHelpers::TriviallyCopyableType& tct,
const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct)
static bool operator== (const ArrayBaseTestsHelpers::TriviallyCopyableType& tct,
const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct)
{
return tct.getValue() == ntct.getValue();
}
bool operator== (const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct,
const ArrayBaseTestsHelpers::TriviallyCopyableType& tct)
static bool operator== (const ArrayBaseTestsHelpers::NonTriviallyCopyableType& ntct,
const ArrayBaseTestsHelpers::TriviallyCopyableType& tct)
{
return tct == ntct;
}


+ 2
- 2
source/modules/juce_core/containers/juce_Variant.cpp View File

@@ -257,8 +257,8 @@ public:
}
private:
static const String* getString (const ValueUnion& data) noexcept { return reinterpret_cast<const String*> (data.stringValue); }
static String* getString (ValueUnion& data) noexcept { return reinterpret_cast<String*> (data.stringValue); }
static const String* getString (const ValueUnion& data) noexcept { return unalignedPointerCast<const String*> (data.stringValue); }
static String* getString (ValueUnion& data) noexcept { return unalignedPointerCast<String*> (data.stringValue); }
};
//==============================================================================


+ 1
- 1
source/modules/juce_core/files/juce_File.h View File

@@ -20,7 +20,7 @@
==============================================================================
*/
#if JUCE_MAC || JUCE_IOS
#if ! DOXYGEN && (JUCE_MAC || JUCE_IOS)
#if __LP64__
using OSType = unsigned int;
#else


+ 9
- 0
source/modules/juce_core/files/juce_RangedDirectoryIterator.cpp View File

@@ -23,6 +23,14 @@
namespace juce
{
float DirectoryEntry::getEstimatedProgress() const
{
if (auto it = iterator.lock())
return it->getEstimatedProgress();
return 0.0f;
}
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
@@ -37,6 +45,7 @@ RangedDirectoryIterator::RangedDirectoryIterator (const File& directory,
wildCard,
whatToLookFor))
{
entry.iterator = iterator;
increment();
}


+ 6
- 0
source/modules/juce_core/files/juce_RangedDirectoryIterator.h View File

@@ -53,7 +53,13 @@ public:
/** True if the item is read-only, false otherwise. */
bool isReadOnly() const { return readOnly; }
/** The estimated proportion of the range that has been visited
by the iterator, from 0.0 to 1.0.
*/
float getEstimatedProgress() const;
private:
std::weak_ptr<DirectoryIterator> iterator;
File file;
Time modTime;
Time creationTime;


+ 1
- 1
source/modules/juce_core/juce_core.h View File

@@ -32,7 +32,7 @@
ID: juce_core
vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE core classes
description: The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.
website: http://www.juce.com/juce


+ 1
- 1
source/modules/juce_core/memory/juce_ByteOrder.h View File

@@ -20,7 +20,7 @@
==============================================================================
*/
#if JUCE_MAC || JUCE_IOS
#if ! DOXYGEN && (JUCE_MAC || JUCE_IOS)
#include <libkern/OSByteOrder.h>
#endif


+ 47
- 7
source/modules/juce_core/memory/juce_Memory.h View File

@@ -39,13 +39,6 @@ inline void zerostruct (Type& structure) noexcept { memset ((v
template <typename Type>
inline void deleteAndZero (Type& pointer) { delete pointer; pointer = nullptr; }
/** A handy function which adds a number of bytes to any type of pointer and returns the result.
This can be useful to avoid casting pointers to a char* and back when you want to move them by
a specific number of bytes,
*/
template <typename Type, typename IntegerType>
inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept { return reinterpret_cast<Type*> (const_cast<char*> (reinterpret_cast<const char*> (basePointer)) + bytes); }
/** A handy function to round up a pointer to the nearest multiple of a given number of bytes.
alignmentBytes must be a power of two. */
template <typename Type, typename IntegerType>
@@ -83,6 +76,53 @@ inline void writeUnaligned (void* dstPtr, Type value) noexcept
memcpy (dstPtr, &value, sizeof (Type));
}
//==============================================================================
/** Casts a pointer to another type via `void*`, which suppresses the cast-align
warning which sometimes arises when casting pointers to types with different
alignment.
You should only use this when you know for a fact that the input pointer points
to a region that has suitable alignment for `Type`, e.g. regions returned from
malloc/calloc that should be suitable for any non-over-aligned type.
*/
template <typename Type, typename std::enable_if<std::is_pointer<Type>::value, int>::type = 0>
inline Type unalignedPointerCast (void* ptr) noexcept
{
return reinterpret_cast<Type> (ptr);
}
/** Casts a pointer to another type via `void*`, which suppresses the cast-align
warning which sometimes arises when casting pointers to types with different
alignment.
You should only use this when you know for a fact that the input pointer points
to a region that has suitable alignment for `Type`, e.g. regions returned from
malloc/calloc that should be suitable for any non-over-aligned type.
*/
template <typename Type, typename std::enable_if<std::is_pointer<Type>::value, int>::type = 0>
inline Type unalignedPointerCast (const void* ptr) noexcept
{
return reinterpret_cast<Type> (ptr);
}
/** A handy function which adds a number of bytes to any type of pointer and returns the result.
This can be useful to avoid casting pointers to a char* and back when you want to move them by
a specific number of bytes,
*/
template <typename Type, typename IntegerType>
inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept
{
return unalignedPointerCast<Type*> (reinterpret_cast<char*> (basePointer) + bytes);
}
/** A handy function which adds a number of bytes to any type of pointer and returns the result.
This can be useful to avoid casting pointers to a char* and back when you want to move them by
a specific number of bytes,
*/
template <typename Type, typename IntegerType>
inline const Type* addBytesToPointer (const Type* basePointer, IntegerType bytes) noexcept
{
return unalignedPointerCast<const Type*> (reinterpret_cast<const char*> (basePointer) + bytes);
}
//==============================================================================
#if JUCE_MAC || JUCE_IOS || DOXYGEN


+ 2
- 2
source/modules/juce_core/native/juce_linux_SystemStats.cpp View File

@@ -215,8 +215,8 @@ double Time::getMillisecondCounterHiRes() noexcept
bool Time::setSystemTimeToThisTime() const
{
timeval t;
t.tv_sec = millisSinceEpoch / 1000;
t.tv_usec = (millisSinceEpoch - t.tv_sec * 1000) * 1000;
t.tv_sec = decltype (timeval::tv_sec) (millisSinceEpoch / 1000);
t.tv_usec = decltype (timeval::tv_usec) ((millisSinceEpoch - t.tv_sec * 1000) * 1000);
return settimeofday (&t, nullptr) == 0;
}


+ 13
- 2
source/modules/juce_core/native/juce_mac_Files.mm View File

@@ -424,13 +424,23 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&
StringArray params;
params.addTokens (parameters, true);
NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease];
NSMutableArray* paramArray = [[NSMutableArray new] autorelease];
for (int i = 0; i < params.size(); ++i)
[paramArray addObject: juceStringToNS (params[i])];
#if (defined MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
auto config = [NSWorkspaceOpenConfiguration configuration];
[config setCreatesNewApplicationInstance: YES];
config.arguments = paramArray;
[workspace openApplicationAtURL: filenameAsURL
configuration: config
completionHandler: nil];
return true;
#else
NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease];
[dict setObject: paramArray
forKey: nsStringLiteral ("NSWorkspaceLaunchConfigurationArguments")];
@@ -438,6 +448,7 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&
options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance
configuration: dict
error: nil];
#endif
}
if (file.exists())


+ 11
- 7
source/modules/juce_core/native/juce_mac_SystemStats.mm View File

@@ -137,11 +137,17 @@ SystemStats::OperatingSystemType SystemStats::getOperatingSystemType()
StringArray parts;
parts.addTokens (getOSXVersion(), ".", StringRef());
jassert (parts[0].getIntValue() == 10);
const int major = parts[1].getIntValue();
jassert (major > 2);
const auto major = parts[0].getIntValue();
const auto minor = parts[1].getIntValue();
return (OperatingSystemType) (major + MacOSX_10_4 - 4);
if (major == 10)
{
jassert (minor > 2);
return (OperatingSystemType) (minor + MacOSX_10_7 - 7);
}
jassert (major == 11 && minor == 0);
return MacOSX_11_0;
#endif
}
@@ -199,10 +205,8 @@ bool SystemStats::isOperatingSystem64Bit()
{
#if JUCE_IOS
return false;
#elif JUCE_64BIT
return true;
#else
return getOperatingSystemType() >= MacOSX_10_6;
return true;
#endif
}


+ 32
- 33
source/modules/juce_core/native/juce_osx_ObjCHelpers.h View File

@@ -193,29 +193,33 @@ NSRect makeNSRect (const RectangleType& r) noexcept
static_cast<CGFloat> (r.getHeight()));
}
#endif
#if JUCE_MAC || JUCE_IOS
// This is necessary as on iOS builds, some arguments may be passed on registers
// depending on the argument type. The re-cast objc_msgSendSuper to a function
// take the same arguments as the target method.
template <typename ReturnValue, typename... Params>
ReturnValue ObjCMsgSendSuper (struct objc_super* s, SEL sel, Params... params)
{
using SuperFn = ReturnValue (*)(struct objc_super*, SEL, Params...);
SuperFn fn = reinterpret_cast<SuperFn> (objc_msgSendSuper);
return fn (s, sel, params...);
}
#if JUCE_INTEL
template <typename T>
struct NeedsStret { static constexpr auto value = sizeof (T) > 16; };
template<>
struct NeedsStret<void> { static constexpr auto value = false; };
// These hacks are a workaround for newer Xcode builds which by default prevent calls to these objc functions..
typedef id (*MsgSendSuperFn) (struct objc_super*, SEL, ...);
inline MsgSendSuperFn getMsgSendSuperFn() noexcept { return (MsgSendSuperFn) (void*) objc_msgSendSuper; }
template <typename T, bool b = NeedsStret<T>::value>
struct MetaSuperFn { static constexpr auto value = objc_msgSendSuper_stret; };
#if ! JUCE_IOS
typedef double (*MsgSendFPRetFn) (id, SEL op, ...);
inline MsgSendFPRetFn getMsgSendFPRetFn() noexcept { return (MsgSendFPRetFn) (void*) objc_msgSend_fpret; }
#endif
template <typename T>
struct MetaSuperFn<T, false> { static constexpr auto value = objc_msgSendSuper; };
#else
template <typename>
struct MetaSuperFn { static constexpr auto value = objc_msgSendSuper; };
#endif
template <typename SuperType, typename ReturnType, typename... Params>
static ReturnType ObjCMsgSendSuper (id self, SEL sel, Params... params)
{
using SuperFn = ReturnType (*) (struct objc_super*, SEL, Params...);
const auto fn = reinterpret_cast<SuperFn> (MetaSuperFn<ReturnType>::value);
objc_super s = { self, [SuperType class] };
return fn (&s, sel, params...);
}
//==============================================================================
struct NSObjectDeleter
{
@@ -236,7 +240,10 @@ struct ObjCClass
~ObjCClass()
{
objc_disposeClassPair (cls);
auto kvoSubclassName = String ("NSKVONotifying_") + class_getName (cls);
if (objc_getClass (kvoSubclassName.toUTF8()) == nullptr)
objc_disposeClassPair (cls);
}
void registerClass()
@@ -287,13 +294,11 @@ struct ObjCClass
jassert (b); ignoreUnused (b);
}
#if JUCE_MAC || JUCE_IOS
static id sendSuperclassMessage (id self, SEL selector)
template <typename ReturnType, typename... Params>
static ReturnType sendSuperclassMessage (id self, SEL sel, Params... params)
{
objc_super s = { self, [SuperclassType class] };
return getMsgSendSuperFn() (&s, selector);
return ObjCMsgSendSuper<SuperclassType, ReturnType, Params...> (self, sel, params...);
}
#endif
template <typename Type>
static Type getIvar (id self, const char* name)
@@ -330,18 +335,14 @@ struct ObjCLifetimeManagedClass : public ObjCClass<NSObject>
addMethod (@selector (dealloc), dealloc, "v@:");
registerClass();
}
static id initWithJuceObject (id _self, SEL, JuceClass* obj)
{
NSObject* self = _self;
objc_super s = { self, [NSObject class] };
self = ObjCMsgSendSuper<NSObject*> (&s, @selector(init));
NSObject* self = sendSuperclassMessage<NSObject*> (_self, @selector (init));
object_setInstanceVariable (self, "cppObject", obj);
return self;
}
@@ -353,11 +354,9 @@ struct ObjCLifetimeManagedClass : public ObjCClass<NSObject>
object_setInstanceVariable (_self, "cppObject", nullptr);
}
objc_super s = { _self, [NSObject class] };
ObjCMsgSendSuper<void> (&s, @selector(dealloc));
sendSuperclassMessage<void> (_self, @selector (dealloc));
}
static ObjCLifetimeManagedClass objCLifetimeManagedClass;
};


+ 4
- 4
source/modules/juce_core/native/juce_posix_IPAddress.h View File

@@ -71,8 +71,8 @@ namespace
{
if (ifa->ifa_addr->sa_family == AF_INET)
{
auto interfaceAddressInfo = reinterpret_cast<sockaddr_in*> (ifa->ifa_addr);
auto broadcastAddressInfo = reinterpret_cast<sockaddr_in*> (ifa->ifa_dstaddr);
auto interfaceAddressInfo = unalignedPointerCast<sockaddr_in*> (ifa->ifa_addr);
auto broadcastAddressInfo = unalignedPointerCast<sockaddr_in*> (ifa->ifa_dstaddr);
if (interfaceAddressInfo->sin_addr.s_addr != INADDR_NONE)
{
@@ -83,8 +83,8 @@ namespace
}
else if (ifa->ifa_addr->sa_family == AF_INET6)
{
interfaceInfo.interfaceAddress = makeAddress (reinterpret_cast<sockaddr_in6*> (ifa->ifa_addr));
interfaceInfo.broadcastAddress = makeAddress (reinterpret_cast<sockaddr_in6*> (ifa->ifa_dstaddr));
interfaceInfo.interfaceAddress = makeAddress (unalignedPointerCast<sockaddr_in6*> (ifa->ifa_addr));
interfaceInfo.broadcastAddress = makeAddress (unalignedPointerCast<sockaddr_in6*> (ifa->ifa_dstaddr));
return true;
}
}


+ 11
- 0
source/modules/juce_core/native/juce_win32_ComSmartPtr.h View File

@@ -134,6 +134,17 @@ public:
return this->QueryInterface (__uuidof (OtherComClass), destObject);
}
template<class OtherComClass>
ComSmartPtr<OtherComClass> getInterface() const
{
ComSmartPtr<OtherComClass> destObject;
if (QueryInterface (destObject) == S_OK)
return destObject;
return nullptr;
}
private:
ComClass* p = nullptr;


+ 0
- 3
source/modules/juce_core/native/juce_win32_SystemStats.cpp View File

@@ -283,9 +283,6 @@ String SystemStats::getOperatingSystemName()
case Android: JUCE_FALLTHROUGH
case iOS: JUCE_FALLTHROUGH
case MacOSX_10_4: JUCE_FALLTHROUGH
case MacOSX_10_5: JUCE_FALLTHROUGH
case MacOSX_10_6: JUCE_FALLTHROUGH
case MacOSX_10_7: JUCE_FALLTHROUGH
case MacOSX_10_8: JUCE_FALLTHROUGH
case MacOSX_10_9: JUCE_FALLTHROUGH


+ 1
- 1
source/modules/juce_core/native/juce_win32_Threads.cpp View File

@@ -448,7 +448,7 @@ public:
if (! isRunning())
break;
Thread::yield();
Thread::sleep (1);
}
else
{


+ 3
- 1
source/modules/juce_core/system/juce_PlatformDefs.h View File

@@ -66,12 +66,14 @@ namespace juce
@see jassert()
*/
#define JUCE_BREAK_IN_DEBUGGER { ::kill (0, SIGTRAP); }
#elif JUCE_MAC && JUCE_CLANG && JUCE_ARM
#define JUCE_BREAK_IN_DEBUGGER { __builtin_debugtrap(); }
#elif JUCE_MSVC
#ifndef __INTEL_COMPILER
#pragma intrinsic (__debugbreak)
#endif
#define JUCE_BREAK_IN_DEBUGGER { __debugbreak(); }
#elif JUCE_GCC || JUCE_MAC
#elif JUCE_INTEL && (JUCE_GCC || JUCE_MAC)
#if JUCE_NO_INLINE_ASM
#define JUCE_BREAK_IN_DEBUGGER { }
#else


+ 1
- 1
source/modules/juce_core/system/juce_StandardHeader.h View File

@@ -29,7 +29,7 @@
*/
#define JUCE_MAJOR_VERSION 6
#define JUCE_MINOR_VERSION 0
#define JUCE_BUILDNUMBER 0
#define JUCE_BUILDNUMBER 4
/** Current JUCE version number.


+ 2
- 3
source/modules/juce_core/system/juce_SystemStats.h View File

@@ -52,9 +52,6 @@ public:
Android = 0x0800,
iOS = 0x1000,
MacOSX_10_4 = MacOSX | 4,
MacOSX_10_5 = MacOSX | 5,
MacOSX_10_6 = MacOSX | 6,
MacOSX_10_7 = MacOSX | 7,
MacOSX_10_8 = MacOSX | 8,
MacOSX_10_9 = MacOSX | 9,
@@ -63,6 +60,8 @@ public:
MacOSX_10_12 = MacOSX | 12,
MacOSX_10_13 = MacOSX | 13,
MacOSX_10_14 = MacOSX | 14,
MacOSX_10_15 = MacOSX | 15,
MacOSX_11_0 = MacOSX | 16,
Win2000 = Windows | 1,
WinXP = Windows | 2,


+ 0
- 1
source/modules/juce_core/system/juce_TargetPlatform.h View File

@@ -58,7 +58,6 @@
//==============================================================================
#if defined (_WIN32) || defined (_WIN64)
#define JUCE_WIN32 1
#define JUCE_WINDOWS 1
#elif defined (JUCE_ANDROID)
#undef JUCE_ANDROID


+ 6
- 6
source/modules/juce_core/text/juce_String.cpp View File

@@ -68,7 +68,7 @@ public:
static CharPointerType createUninitialisedBytes (size_t numBytes)
{
numBytes = (numBytes + 3) & ~(size_t) 3;
auto s = reinterpret_cast<StringHolder*> (new char [sizeof (StringHolder) - sizeof (CharType) + numBytes]);
auto s = unalignedPointerCast<StringHolder*> (new char [sizeof (StringHolder) - sizeof (CharType) + numBytes]);
s->refCount.value = 0;
s->allocatedNumBytes = numBytes;
return CharPointerType (s->text);
@@ -210,7 +210,7 @@ private:
static StringHolder* bufferFromText (const CharPointerType text) noexcept
{
// (Can't use offsetof() here because of warnings about this not being a POD)
return reinterpret_cast<StringHolder*> (reinterpret_cast<char*> (text.getAddress())
return unalignedPointerCast<StringHolder*> (reinterpret_cast<char*> (text.getAddress())
- (reinterpret_cast<size_t> (reinterpret_cast<StringHolder*> (128)->text) - 128));
}
@@ -1991,7 +1991,7 @@ String String::createStringFromData (const void* const unknownData, int size)
StringCreationHelper builder ((size_t) numChars);
auto src = reinterpret_cast<const uint16*> (data + 2);
auto src = unalignedPointerCast<const uint16*> (data + 2);
if (CharPointer_UTF16::isByteOrderMarkBigEndian (data))
{
@@ -2061,19 +2061,19 @@ struct StringEncodingConverter
template <>
struct StringEncodingConverter<CharPointer_UTF8, CharPointer_UTF8>
{
static CharPointer_UTF8 convert (const String& source) noexcept { return CharPointer_UTF8 (reinterpret_cast<CharPointer_UTF8::CharType*> (source.getCharPointer().getAddress())); }
static CharPointer_UTF8 convert (const String& source) noexcept { return CharPointer_UTF8 (unalignedPointerCast<CharPointer_UTF8::CharType*> (source.getCharPointer().getAddress())); }
};
template <>
struct StringEncodingConverter<CharPointer_UTF16, CharPointer_UTF16>
{
static CharPointer_UTF16 convert (const String& source) noexcept { return CharPointer_UTF16 (reinterpret_cast<CharPointer_UTF16::CharType*> (source.getCharPointer().getAddress())); }
static CharPointer_UTF16 convert (const String& source) noexcept { return CharPointer_UTF16 (unalignedPointerCast<CharPointer_UTF16::CharType*> (source.getCharPointer().getAddress())); }
};
template <>
struct StringEncodingConverter<CharPointer_UTF32, CharPointer_UTF32>
{
static CharPointer_UTF32 convert (const String& source) noexcept { return CharPointer_UTF32 (reinterpret_cast<CharPointer_UTF32::CharType*> (source.getCharPointer().getAddress())); }
static CharPointer_UTF32 convert (const String& source) noexcept { return CharPointer_UTF32 (unalignedPointerCast<CharPointer_UTF32::CharType*> (source.getCharPointer().getAddress())); }
};
CharPointer_UTF8 String::toUTF8() const { return StringEncodingConverter<CharPointerType, CharPointer_UTF8 >::convert (*this); }


+ 1
- 1
source/modules/juce_core/text/juce_String.h View File

@@ -20,7 +20,7 @@
==============================================================================
*/
#if JUCE_MAC || JUCE_IOS
#if ! DOXYGEN && (JUCE_MAC || JUCE_IOS)
// Annoyingly we can only forward-declare a typedef by forward-declaring the
// aliased type
#if __has_attribute(objc_bridge)


+ 1
- 1
source/modules/juce_data_structures/juce_data_structures.h View File

@@ -35,7 +35,7 @@
ID: juce_data_structures
vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE data model helper classes
description: Classes for undo/redo management, and smart data structures.
website: http://www.juce.com/juce


+ 1
- 1
source/modules/juce_events/juce_events.h View File

@@ -32,7 +32,7 @@
ID: juce_events
vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE message and event handling classes
description: Classes for running an application's main event loop and sending/receiving messages, timers, etc.
website: http://www.juce.com/juce


+ 5
- 1
source/modules/juce_events/native/juce_mac_MessageManager.mm View File

@@ -144,7 +144,11 @@ private:
{
if (notification.userInfo != nil)
{
NSUserNotification* userNotification = [notification.userInfo objectForKey: nsStringLiteral ("NSApplicationLaunchUserNotificationKey")];
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);


+ 0
- 2
source/modules/juce_graphics/fonts/juce_Font.h View File

@@ -303,7 +303,6 @@ public:
/** Returns true if the font is underlined. */
bool isUnderlined() const noexcept;
//==============================================================================
/** Returns the font's horizontal scale.
A value of 1.0 is the normal scale, less than this will be narrower, greater
@@ -464,7 +463,6 @@ public:
*/
static Font fromString (const String& fontDescription);
private:
//==============================================================================
class SharedFontInternal;


+ 14
- 4
source/modules/juce_graphics/geometry/juce_Point.h View File

@@ -120,12 +120,22 @@ public:
Point& operator/= (Point<OtherType> other) noexcept { *this = *this / other; return *this; }
/** Returns a point whose coordinates are multiplied by a given scalar value. */
template <typename FloatType>
constexpr Point operator* (FloatType multiplier) const noexcept { return Point ((ValueType) ((FloatType) x * multiplier), (ValueType) ((FloatType) y * multiplier)); }
template <typename OtherType>
constexpr Point operator* (OtherType multiplier) const noexcept
{
using CommonType = typename std::common_type<ValueType, OtherType>::type;
return Point ((ValueType) ((CommonType) x * (CommonType) multiplier),
(ValueType) ((CommonType) y * (CommonType) multiplier));
}
/** Returns a point whose coordinates are divided by a given scalar value. */
template <typename FloatType>
constexpr Point operator/ (FloatType divisor) const noexcept { return Point ((ValueType) ((FloatType) x / divisor), (ValueType) ((FloatType) y / divisor)); }
template <typename OtherType>
constexpr Point operator/ (OtherType divisor) const noexcept
{
using CommonType = typename std::common_type<ValueType, OtherType>::type;
return Point ((ValueType) ((CommonType) x / (CommonType) divisor),
(ValueType) ((CommonType) y / (CommonType) divisor));
}
/** Multiplies the point's coordinates by a scalar value. */
template <typename FloatType>


+ 0
- 4
source/modules/juce_graphics/juce_graphics.cpp View File

@@ -75,10 +75,6 @@
#import <QuartzCore/QuartzCore.h>
#import <CoreText/CoreText.h>
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_3_2
#error "JUCE no longer supports targets earlier than iOS 3.2"
#endif
#elif JUCE_LINUX
#ifndef JUCE_USE_FREETYPE
#define JUCE_USE_FREETYPE 1


+ 2
- 2
source/modules/juce_graphics/juce_graphics.h View File

@@ -35,7 +35,7 @@
ID: juce_graphics
vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE graphics classes
description: Classes for 2D vector graphics, image loading/saving, font handling, etc.
website: http://www.juce.com/juce
@@ -79,7 +79,7 @@
/** Config: JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING
Setting this flag will turn off CoreGraphics font smoothing, which some people
Setting this flag will turn off CoreGraphics font smoothing on macOS, which some people
find makes the text too 'fat' for their taste.
*/
#ifndef JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING


+ 34
- 34
source/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm View File

@@ -189,6 +189,15 @@ ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int widt
return *new CoreGraphicsPixelData (format == Image::RGB ? Image::ARGB : format, width, height, clearImage);
}
//==============================================================================
struct ScopedCGContextState
{
explicit ScopedCGContextState (CGContextRef c) : context (c) { CGContextSaveGState (context); }
~ScopedCGContextState() { CGContextRestoreGState (context); }
CGContextRef context;
};
//==============================================================================
CoreGraphicsContext::CoreGraphicsContext (CGContextRef c, float h)
: context (c),
@@ -198,15 +207,18 @@ CoreGraphicsContext::CoreGraphicsContext (CGContextRef c, float h)
CGContextRetain (context.get());
CGContextSaveGState (context.get());
#if JUCE_MAC
bool enableFontSmoothing
#if JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING
= false;
#else
= true;
#endif
#if JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING
= false;
#else
= true;
#endif
CGContextSetShouldSmoothFonts (context.get(), enableFontSmoothing);
CGContextSetAllowsFontSmoothing (context.get(), enableFontSmoothing);
#endif
CGContextSetShouldAntialias (context.get(), true);
CGContextSetBlendMode (context.get(), kCGBlendModeNormal);
rgbColourSpace.reset (CGColorSpaceCreateWithName (kCGColorSpaceSRGB));
@@ -455,26 +467,23 @@ void CoreGraphicsContext::fillCGRect (const CGRect& cgRect, bool replaceExisting
{
CGContextFillRect (context.get(), cgRect);
}
else if (state->fillType.isGradient())
{
CGContextSaveGState (context.get());
CGContextClipToRect (context.get(), cgRect);
drawGradient();
CGContextRestoreGState (context.get());
}
else
{
CGContextSaveGState (context.get());
ScopedCGContextState scopedState (context.get());
CGContextClipToRect (context.get(), cgRect);
drawImage (state->fillType.image, state->fillType.transform, true);
CGContextRestoreGState (context.get());
if (state->fillType.isGradient())
drawGradient();
else
drawImage (state->fillType.image, state->fillType.transform, true);
}
}
}
void CoreGraphicsContext::fillPath (const Path& path, const AffineTransform& transform)
{
CGContextSaveGState (context.get());
ScopedCGContextState scopedState (context.get());
if (state->fillType.isColour())
{
@@ -501,8 +510,6 @@ void CoreGraphicsContext::fillPath (const Path& path, const AffineTransform& tra
else
drawImage (state->fillType.image, state->fillType.transform, true);
}
CGContextRestoreGState (context.get());
}
void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTransform& transform)
@@ -519,7 +526,7 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans
: rgbColourSpace.get();
auto image = detail::ImagePtr { CoreGraphicsPixelData::getCachedImageRef (sourceImage, colourSpace) };
CGContextSaveGState (context.get());
ScopedCGContextState scopedState (context.get());
CGContextSetAlpha (context.get(), state->fillType.getOpacity());
flip();
@@ -563,8 +570,6 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans
{
CGContextDrawImage (context.get(), imageRect, image.get());
}
CGContextRestoreGState (context.get());
}
//==============================================================================
@@ -588,19 +593,16 @@ void CoreGraphicsContext::fillRectList (const RectangleList<float>& list)
{
CGContextFillRects (context.get(), rects, num);
}
else if (state->fillType.isGradient())
{
CGContextSaveGState (context.get());
CGContextClipToRects (context.get(), rects, num);
drawGradient();
CGContextRestoreGState (context.get());
}
else
{
CGContextSaveGState (context.get());
ScopedCGContextState scopedState (context.get());
CGContextClipToRects (context.get(), rects, num);
drawImage (state->fillType.image, state->fillType.transform, true);
CGContextRestoreGState (context.get());
if (state->fillType.isGradient())
drawGradient();
else
drawImage (state->fillType.image, state->fillType.transform, true);
}
}
@@ -650,7 +652,7 @@ void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& tra
}
else
{
CGContextSaveGState (context.get());
ScopedCGContextState scopedState (context.get());
flip();
applyTransform (transform);
@@ -662,8 +664,6 @@ void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& tra
CGGlyph glyphs[1] = { (CGGlyph) glyphNumber };
CGPoint positions[1] = { { 0.0f, 0.0f } };
CGContextShowGlyphsAtPositions (context.get(), glyphs, positions, 1);
CGContextRestoreGState (context.get());
}
}
else


+ 29
- 0
source/modules/juce_graphics/native/juce_mac_Fonts.mm View File

@@ -234,6 +234,15 @@ namespace CoreTextTypeLayout
ctFontRef = getFontWithPointSize (ctFontRef, attr.font.getHeight() * getHeightToPointsFactor (ctFontRef));
CFAttributedStringSetAttribute (attribString, range, kCTFontAttributeName, ctFontRef);
if (attr.font.isUnderlined())
{
auto underline = kCTUnderlineStyleSingle;
auto numberRef = CFNumberCreate (nullptr, kCFNumberIntType, &underline);
CFAttributedStringSetAttribute (attribString, range, kCTUnderlineStyleAttributeName, numberRef);
CFRelease (numberRef);
}
auto extraKerning = attr.font.getExtraKerningFactor();
if (extraKerning != 0)
@@ -463,6 +472,26 @@ namespace CoreTextTypeLayout
String::fromCFString (cfsFontStyle),
(float) (CTFontGetSize (ctRunFont) / fontHeightToPointsFactor));
auto isUnderlined = [&]
{
CFNumberRef underlineStyle;
if (CFDictionaryGetValueIfPresent (runAttributes, kCTUnderlineStyleAttributeName, (const void**) &underlineStyle))
{
if (CFGetTypeID (underlineStyle) == CFNumberGetTypeID())
{
int value = 0;
CFNumberGetValue (underlineStyle, kCFNumberLongType, (void*) &value);
return value != 0;
}
}
return false;
}();
glyphRun->font.setUnderline (isUnderlined);
CFRelease (cfsFontStyle);
CFRelease (cfsFontFamily);
}


+ 26
- 8
source/modules/juce_gui_basics/components/juce_Component.cpp View File

@@ -216,6 +216,22 @@ struct ScalingHelpers
roundToInt ((float) pos.getHeight() * scale)) : pos;
}
static Rectangle<float> unscaledScreenPosToScaled (float scale, Rectangle<float> pos) noexcept
{
return scale != 1.0f ? Rectangle<float> (pos.getX() / scale,
pos.getY() / scale,
pos.getWidth() / scale,
pos.getHeight() / scale) : pos;
}
static Rectangle<float> scaledScreenPosToUnscaled (float scale, Rectangle<float> pos) noexcept
{
return scale != 1.0f ? Rectangle<float> (pos.getX() * scale,
pos.getY() * scale,
pos.getWidth() * scale,
pos.getHeight() * scale) : pos;
}
template <typename PointOrRect>
static PointOrRect unscaledScreenPosToScaled (PointOrRect pos) noexcept
{
@@ -766,7 +782,7 @@ bool Component::isOpaque() const noexcept
//==============================================================================
struct StandardCachedComponentImage : public CachedComponentImage
{
StandardCachedComponentImage (Component& c) noexcept : owner (c), scale (1.0f) {}
StandardCachedComponentImage (Component& c) noexcept : owner (c) {}
void paint (Graphics& g) override
{
@@ -820,7 +836,7 @@ private:
Image image;
RectangleList<int> validArea;
Component& owner;
float scale;
float scale = 1.0f;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandardCachedComponentImage)
};
@@ -1051,13 +1067,15 @@ int Component::getScreenY() const { return getScreenPositi
Point<int> Component::getScreenPosition() const { return localPointToGlobal (Point<int>()); }
Rectangle<int> Component::getScreenBounds() const { return localAreaToGlobal (getLocalBounds()); }
Point<int> Component::getLocalPoint (const Component* source, Point<int> point) const { return ComponentHelpers::convertCoordinate (this, source, point); }
Point<float> Component::getLocalPoint (const Component* source, Point<float> point) const { return ComponentHelpers::convertCoordinate (this, source, point); }
Rectangle<int> Component::getLocalArea (const Component* source, Rectangle<int> area) const { return ComponentHelpers::convertCoordinate (this, source, area); }
Point<int> Component::getLocalPoint (const Component* source, Point<int> point) const { return ComponentHelpers::convertCoordinate (this, source, point); }
Point<float> Component::getLocalPoint (const Component* source, Point<float> point) const { return ComponentHelpers::convertCoordinate (this, source, point); }
Rectangle<int> Component::getLocalArea (const Component* source, Rectangle<int> area) const { return ComponentHelpers::convertCoordinate (this, source, area); }
Rectangle<float> Component::getLocalArea (const Component* source, Rectangle<float> area) const { return ComponentHelpers::convertCoordinate (this, source, area); }
Point<int> Component::localPointToGlobal (Point<int> point) const { return ComponentHelpers::convertCoordinate (nullptr, this, point); }
Point<float> Component::localPointToGlobal (Point<float> point) const { return ComponentHelpers::convertCoordinate (nullptr, this, point); }
Rectangle<int> Component::localAreaToGlobal (Rectangle<int> area) const { return ComponentHelpers::convertCoordinate (nullptr, this, area); }
Point<int> Component::localPointToGlobal (Point<int> point) const { return ComponentHelpers::convertCoordinate (nullptr, this, point); }
Point<float> Component::localPointToGlobal (Point<float> point) const { return ComponentHelpers::convertCoordinate (nullptr, this, point); }
Rectangle<int> Component::localAreaToGlobal (Rectangle<int> area) const { return ComponentHelpers::convertCoordinate (nullptr, this, area); }
Rectangle<float> Component::localAreaToGlobal (Rectangle<float> area) const { return ComponentHelpers::convertCoordinate (nullptr, this, area); }
//==============================================================================
void Component::setBounds (int x, int y, int w, int h)


+ 22
- 0
source/modules/juce_gui_basics/components/juce_Component.h View File

@@ -369,6 +369,19 @@ public:
Rectangle<int> getLocalArea (const Component* sourceComponent,
Rectangle<int> areaRelativeToSourceComponent) const;
/** Converts a rectangle to be relative to this component's coordinate space.
This takes a rectangle that is relative to a different component, and returns its position relative
to this component. If the sourceComponent parameter is null, the source rectangle is assumed to be
a screen coordinate.
If you've used setTransform() to apply one or more transforms to components, then the source rectangle
may not actually be rectangular when converted to the target space, so in that situation this will return
the smallest rectangle that fully contains the transformed area.
*/
Rectangle<float> getLocalArea (const Component* sourceComponent,
Rectangle<float> areaRelativeToSourceComponent) const;
/** Converts a point relative to this component's top-left into a screen coordinate.
@see getLocalPoint, localAreaToGlobal
*/
@@ -388,6 +401,15 @@ public:
*/
Rectangle<int> localAreaToGlobal (Rectangle<int> localArea) const;
/** Converts a rectangle from this component's coordinate space to a screen coordinate.
If you've used setTransform() to apply one or more transforms to components, then the source rectangle
may not actually be rectangular when converted to the target space, so in that situation this will return
the smallest rectangle that fully contains the transformed area.
@see getLocalPoint, localPointToGlobal
*/
Rectangle<float> localAreaToGlobal (Rectangle<float> localArea) const;
//==============================================================================
/** Moves the component to a new position.


+ 42
- 34
source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp View File

@@ -368,7 +368,7 @@ public:
if (parseNextNumber (d, num, false))
{
auto angle = degreesToRadians (num.getFloatValue());
auto angle = degreesToRadians (parseSafeFloat (num));
if (parseNextFlag (d, flagValue))
{
@@ -460,7 +460,7 @@ private:
}
//==============================================================================
void parseSubElements (const XmlPath& xml, DrawableComposite& parentDrawable, const bool shouldParseClip = true)
void parseSubElements (const XmlPath& xml, DrawableComposite& parentDrawable, bool shouldParseClip = true)
{
forEachXmlChildElement (*xml, e)
{
@@ -618,7 +618,7 @@ private:
line.lineTo (x2, y2);
}
void parsePolygon (const XmlPath& xml, const bool isPolyline, Path& path) const
void parsePolygon (const XmlPath& xml, bool isPolyline, Path& path) const
{
auto pointsAtt = xml->getStringAttribute ("points");
auto points = pointsAtt.getCharPointer();
@@ -683,7 +683,7 @@ private:
//==============================================================================
Drawable* parseShape (const XmlPath& xml, Path& path,
const bool shouldParseTransform = true,
bool shouldParseTransform = true,
AffineTransform* additonalTransform = nullptr) const
{
if (shouldParseTransform && xml->hasAttribute ("transform"))
@@ -835,14 +835,14 @@ private:
auto col = parseColour (fillXml.getChild (e), "stop-color", Colours::black);
auto opacity = getStyleAttribute (fillXml.getChild (e), "stop-opacity", "1");
col = col.withMultipliedAlpha (jlimit (0.0f, 1.0f, opacity.getFloatValue()));
col = col.withMultipliedAlpha (jlimit (0.0f, 1.0f, parseSafeFloat (opacity)));
double offset = e->getDoubleAttribute ("offset");
auto offset = parseSafeFloat (e->getStringAttribute ("offset"));
if (e->getStringAttribute ("offset").containsChar ('%'))
offset *= 0.01;
offset *= 0.01f;
cg.addColour (jlimit (0.0, 1.0, offset), col);
cg.addColour (jlimit (0.0f, 1.0f, offset), col);
result = true;
}
}
@@ -983,10 +983,10 @@ private:
float opacity = 1.0f;
if (overallOpacity.isNotEmpty())
opacity = jlimit (0.0f, 1.0f, overallOpacity.getFloatValue());
opacity = jlimit (0.0f, 1.0f, parseSafeFloat (overallOpacity));
if (fillOpacity.isNotEmpty())
opacity *= (jlimit (0.0f, 1.0f, fillOpacity.getFloatValue()));
opacity *= jlimit (0.0f, 1.0f, parseSafeFloat (fillOpacity));
String fill (getStyleAttribute (xml, fillAttribute));
String urlID = parseURL (fill);
@@ -1035,11 +1035,10 @@ private:
}
//==============================================================================
Drawable* useText (const XmlPath& xml) const
{
auto translation = AffineTransform::translation ((float) xml->getDoubleAttribute ("x", 0.0),
(float) xml->getDoubleAttribute ("y", 0.0));
auto translation = AffineTransform::translation (parseSafeFloat (xml->getStringAttribute ("x")),
parseSafeFloat (xml->getStringAttribute ("y")));
UseTextOp op = { this, &translation, nullptr };
@@ -1099,7 +1098,7 @@ private:
dt->setTransform (transform);
dt->setColour (parseColour (xml, "fill", Colours::black)
.withMultipliedAlpha (getStyleAttribute (xml, "fill-opacity", "1").getFloatValue()));
.withMultipliedAlpha (parseSafeFloat (getStyleAttribute (xml, "fill-opacity", "1"))));
Rectangle<float> bounds (xCoords[0], yCoords[0] - font.getAscent(),
font.getStringWidthFloat (text), font.getHeight());
@@ -1138,8 +1137,8 @@ private:
//==============================================================================
Drawable* useImage (const XmlPath& xml) const
{
auto translation = AffineTransform::translation ((float) xml->getDoubleAttribute ("x", 0.0),
(float) xml->getDoubleAttribute ("y", 0.0));
auto translation = AffineTransform::translation (parseSafeFloat (xml->getStringAttribute ("x")),
parseSafeFloat (xml->getStringAttribute ("y")));
UseImageOp op = { this, &translation, nullptr };
@@ -1210,10 +1209,13 @@ private:
setCommonAttributes (*di, xml);
Rectangle<float> imageBounds ((float) xml->getDoubleAttribute ("x", 0.0), (float) xml->getDoubleAttribute ("y", 0.0),
(float) xml->getDoubleAttribute ("width", image.getWidth()), (float) xml->getDoubleAttribute ("height", image.getHeight()));
Rectangle<float> imageBounds (parseSafeFloat (xml->getStringAttribute ("x")),
parseSafeFloat (xml->getStringAttribute ("y")),
parseSafeFloat (xml->getStringAttribute ("width", String (image.getWidth()))),
parseSafeFloat (xml->getStringAttribute ("height", String (image.getHeight()))));
di->setImage (image.rescaled ((int) imageBounds.getWidth(), (int) imageBounds.getHeight()));
di->setImage (image.rescaled ((int) imageBounds.getWidth(),
(int) imageBounds.getHeight()));
di->setTransformToFit (imageBounds, RectanglePlacement (parsePlacementFlags (xml->getStringAttribute ("preserveAspectRatio").trim())));
@@ -1237,7 +1239,7 @@ private:
}
//==============================================================================
bool parseCoord (String::CharPointerType& s, float& value, const bool allowUnits, const bool isX) const
bool parseCoord (String::CharPointerType& s, float& value, bool allowUnits, bool isX) const
{
String number;
@@ -1251,13 +1253,13 @@ private:
return true;
}
bool parseCoords (String::CharPointerType& s, Point<float>& p, const bool allowUnits) const
bool parseCoords (String::CharPointerType& s, Point<float>& p, bool allowUnits) const
{
return parseCoord (s, p.x, allowUnits, true)
&& parseCoord (s, p.y, allowUnits, false);
}
bool parseCoordsOrSkip (String::CharPointerType& s, Point<float>& p, const bool allowUnits) const
bool parseCoordsOrSkip (String::CharPointerType& s, Point<float>& p, bool allowUnits) const
{
if (parseCoords (s, p, allowUnits))
return true;
@@ -1268,8 +1270,8 @@ private:
float getCoordLength (const String& s, const float sizeForProportions) const noexcept
{
float n = s.getFloatValue();
const int len = s.length();
auto n = parseSafeFloat (s);
auto len = s.length();
if (len > 2)
{
@@ -1293,7 +1295,7 @@ private:
return getCoordLength (xml->getStringAttribute (attName), sizeForProportions);
}
void getCoordList (Array<float>& coords, const String& list, bool allowUnits, const bool isX) const
void getCoordList (Array<float>& coords, const String& list, bool allowUnits, bool isX) const
{
auto text = list.getCharPointer();
float value;
@@ -1302,6 +1304,12 @@ private:
coords.add (value);
}
static float parseSafeFloat (const String& s)
{
auto n = s.getFloatValue();
return (std::isnan (n) || std::isinf (n)) ? 0.0f : n;
}
//==============================================================================
void parseCSSStyle (const XmlPath& xml)
{
@@ -1452,7 +1460,7 @@ private:
return CharacterFunctions::isDigit (c) || c == '-' || c == '+';
}
static bool parseNextNumber (String::CharPointerType& text, String& value, const bool allowUnits)
static bool parseNextNumber (String::CharPointerType& text, String& value, bool allowUnits)
{
auto s = text;
@@ -1574,21 +1582,21 @@ private:
auto alpha = [&tokens, &text]
{
if ((text.startsWith ("rgba") || text.startsWith ("hsla")) && tokens.size() == 4)
return tokens[3].getFloatValue();
return parseSafeFloat (tokens[3]);
return 1.0f;
}();
if (text.startsWith ("hsl"))
return Colour::fromHSL ((float) (tokens[0].getDoubleValue() / 360.0),
(float) (tokens[1].getDoubleValue() / 100.0),
(float) (tokens[2].getDoubleValue() / 100.0),
return Colour::fromHSL (parseSafeFloat (tokens[0]) / 360.0f,
parseSafeFloat (tokens[1]) / 100.0f,
parseSafeFloat (tokens[2]) / 100.0f,
alpha);
if (tokens[0].containsChar ('%'))
return Colour ((uint8) roundToInt (2.55 * tokens[0].getDoubleValue()),
(uint8) roundToInt (2.55 * tokens[1].getDoubleValue()),
(uint8) roundToInt (2.55 * tokens[2].getDoubleValue()),
return Colour ((uint8) roundToInt (2.55f * parseSafeFloat (tokens[0])),
(uint8) roundToInt (2.55f * parseSafeFloat (tokens[1])),
(uint8) roundToInt (2.55f * parseSafeFloat (tokens[2])),
alpha);
return Colour ((uint8) tokens[0].getIntValue(),
@@ -1623,7 +1631,7 @@ private:
float numbers[6];
for (int i = 0; i < numElementsInArray (numbers); ++i)
numbers[i] = tokens[i].getFloatValue();
numbers[i] = parseSafeFloat (tokens[i]);
AffineTransform trans;


+ 1
- 1
source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp View File

@@ -367,7 +367,7 @@ void FileBrowserComponent::lookAndFeelChanged()
currentPathBox.setColour (ComboBox::arrowColourId, findColour (currentPathBoxArrowColourId));
filenameBox.setColour (TextEditor::backgroundColourId, findColour (filenameBoxBackgroundColourId));
filenameBox.setColour (TextEditor::textColourId, findColour (filenameBoxTextColourId));
filenameBox.applyColourToAllText (findColour (filenameBoxTextColourId));
resized();
repaint();


+ 2
- 2
source/modules/juce_gui_basics/juce_gui_basics.h View File

@@ -35,7 +35,7 @@
ID: juce_gui_basics
vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE GUI core classes
description: Basic user-interface components and related classes.
website: http://www.juce.com/juce
@@ -43,7 +43,7 @@
dependencies: juce_graphics juce_data_structures
OSXFrameworks: Cocoa Carbon QuartzCore
iOSFrameworks: UIKit MobileCoreServices
iOSFrameworks: UIKit CoreServices
END_JUCE_MODULE_DECLARATION


+ 30
- 8
source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp View File

@@ -382,11 +382,11 @@ struct MenuWindow : public Component
{
if (key.isKeyCode (KeyPress::downKey))
{
selectNextItem (1);
selectNextItem (MenuSelectionDirection::forwards);
}
else if (key.isKeyCode (KeyPress::upKey))
{
selectNextItem (-1);
selectNextItem (MenuSelectionDirection::backwards);
}
else if (key.isKeyCode (KeyPress::leftKey))
{
@@ -414,14 +414,14 @@ struct MenuWindow : public Component
if (showSubMenuFor (currentChild))
{
if (isSubMenuVisible())
activeSubMenu->selectNextItem (0);
activeSubMenu->selectNextItem (MenuSelectionDirection::current);
}
else if (componentAttachedTo != nullptr)
{
componentAttachedTo->keyPressed (key);
}
}
else if (key.isKeyCode (KeyPress::returnKey))
else if (key.isKeyCode (KeyPress::returnKey) || key.isKeyCode (KeyPress::spaceKey))
{
triggerCurrentlyHighlightedItem();
}
@@ -948,24 +948,46 @@ struct MenuWindow : public Component
}
}
void selectNextItem (int delta)
enum class MenuSelectionDirection
{
forwards,
backwards,
current
};
void selectNextItem (MenuSelectionDirection direction)
{
disableTimerUntilMouseMoves();
auto start = jmax (0, items.indexOf (currentChild));
auto start = [&]
{
auto index = items.indexOf (currentChild);
if (index >= 0)
return index;
return direction == MenuSelectionDirection::backwards ? items.size() - 1
: 0;
}();
auto preIncrement = (direction != MenuSelectionDirection::current && currentChild != nullptr);
for (int i = items.size(); --i >= 0;)
{
start += delta;
if (preIncrement)
start += (direction == MenuSelectionDirection::backwards ? -1 : 1);
if (auto* mic = items.getUnchecked ((start + items.size()) % items.size()))
{
if (canBeTriggered (mic->item) || hasActiveSubMenu (mic->item))
{
setCurrentlyHighlightedChild (mic);
break;
return;
}
}
if (! preIncrement)
preIncrement = true;
}
}


+ 2
- 2
source/modules/juce_gui_basics/misc/juce_JUCESplashScreen.cpp View File

@@ -29,12 +29,12 @@ namespace juce
/*
==============================================================================
In accordance with the terms of the JUCE 5 End-Use License Agreement, the
In accordance with the terms of the JUCE 6 End-Use License Agreement, the
JUCE Code in SECTION A cannot be removed, changed or otherwise rendered
ineffective unless you have a JUCE Indie or Pro license, or are using JUCE
under the GPL v3 license.
End User License Agreement: www.juce.com/juce-5-licence
End User License Agreement: www.juce.com/juce-6-licence
==============================================================================
*/


+ 2
- 2
source/modules/juce_gui_basics/misc/juce_JUCESplashScreen.h View File

@@ -26,12 +26,12 @@
/*
==============================================================================
In accordance with the terms of the JUCE 5 End-Use License Agreement, the
In accordance with the terms of the JUCE 6 End-Use License Agreement, the
JUCE Code in SECTION A cannot be removed, changed or otherwise rendered
ineffective unless you have a JUCE Indie or Pro license, or are using JUCE
under the GPL v3 license.
End User License Agreement: www.juce.com/juce-5-licence
End User License Agreement: www.juce.com/juce-6-licence
==============================================================================
*/


+ 1
- 0
source/modules/juce_gui_basics/native/juce_ios_ContentSharer.cpp View File

@@ -124,6 +124,7 @@ private:
setBounds (bounds);
setAlwaysOnTop (true);
setVisible (true);
addToDesktop (0);
enterModalState (true,


+ 77
- 36
source/modules/juce_gui_basics/native/juce_ios_FileChooser.mm View File

@@ -26,19 +26,22 @@
namespace juce
{
class FileChooser::Native : private Component,
public FileChooser::Pimpl
class FileChooser::Native : public FileChooser::Pimpl,
public Component,
private AsyncUpdater
{
public:
Native (FileChooser& fileChooser, int flags)
: owner (fileChooser)
{
String firstFileExtension;
static FileChooserDelegateClass cls;
delegate.reset ([cls.createInstance() init]);
static FileChooserDelegateClass delegateClass;
delegate.reset ([delegateClass.createInstance() init]);
FileChooserDelegateClass::setOwner (delegate.get(), this);
static FileChooserControllerClass controllerClass;
auto* controllerClassInstance = controllerClass.createInstance();
String firstFileExtension;
auto utTypeArray = createNSArrayFromStringArray (getUTTypesForWildcards (owner.filters, firstFileExtension));
if ((flags & FileBrowserComponent::saveMode) != 0)
@@ -69,47 +72,51 @@ public:
}
auto url = [[NSURL alloc] initFileURLWithPath: juceStringToNS (currentFileOrDirectory.getFullPathName())];
controller.reset ([[UIDocumentPickerViewController alloc] initWithURL: url
inMode: pickerMode]);
controller.reset ([controllerClassInstance initWithURL: url
inMode: pickerMode]);
[url release];
}
else
{
controller.reset ([[UIDocumentPickerViewController alloc] initWithDocumentTypes: utTypeArray
inMode: UIDocumentPickerModeOpen]);
controller.reset ([controllerClassInstance initWithDocumentTypes: utTypeArray
inMode: UIDocumentPickerModeOpen]);
}
FileChooserControllerClass::setOwner (controller.get(), this);
[controller.get() setDelegate: delegate.get()];
[controller.get() setModalTransitionStyle: UIModalTransitionStyleCrossDissolve];
setOpaque (false);
if (SystemStats::isRunningInAppExtensionSandbox())
if (fileChooser.parent != nullptr)
{
if (fileChooser.parent != nullptr)
{
[controller.get() setModalPresentationStyle:UIModalPresentationFullScreen];
[controller.get() setModalPresentationStyle: UIModalPresentationFullScreen];
auto chooserBounds = fileChooser.parent->getBounds();
setBounds (chooserBounds);
auto chooserBounds = fileChooser.parent->getBounds();
setBounds (chooserBounds);
setAlwaysOnTop (true);
fileChooser.parent->addAndMakeVisible (this);
}
else
setAlwaysOnTop (true);
fileChooser.parent->addAndMakeVisible (this);
}
else
{
if (SystemStats::isRunningInAppExtensionSandbox())
{
// Opening a native top-level window in an AUv3 is not allowed (sandboxing). You need to specify a
// parent component (for example your editor) to parent the native file chooser window. To do this
// specify a parent component in the FileChooser's constructor!
jassert (fileChooser.parent != nullptr);
jassertfalse;
return;
}
}
else
{
auto chooserBounds = Desktop::getInstance().getDisplays().getMainDisplay().userArea;
setBounds (chooserBounds);
setAlwaysOnTop (true);
setVisible (true);
addToDesktop (0);
}
}
@@ -131,8 +138,6 @@ public:
#endif
}
private:
//==============================================================================
void parentHierarchyChanged() override
{
auto* newPeer = dynamic_cast<UIViewComponentPeer*> (getPeer());
@@ -141,11 +146,23 @@ private:
{
peer = newPeer;
if (auto* parentController = peer->controller)
[parentController showViewController: controller.get() sender: parentController];
if (peer != nullptr)
{
if (auto* parentController = peer->controller)
[parentController showViewController: controller.get() sender: parentController];
peer->toFront (false);
}
}
}
private:
//==============================================================================
void handleAsyncUpdate() override
{
pickerWasCancelled();
}
//==============================================================================
static StringArray getUTTypesForWildcards (const String& filterWildcards, String& firstExtension)
{
@@ -182,7 +199,9 @@ private:
}
}
else
{
result.add ("public.data");
}
return result;
}
@@ -207,6 +226,8 @@ private:
//==============================================================================
void didPickDocumentAtURL (NSURL* url)
{
cancelPendingUpdate();
bool isWriting = controller.get().documentPickerMode == UIDocumentPickerModeExportToService
| controller.get().documentPickerMode == UIDocumentPickerModeMoveToService;
@@ -267,9 +288,9 @@ private:
void pickerWasCancelled()
{
Array<URL> chooserResults;
cancelPendingUpdate();
owner.finished (chooserResults);
owner.finished ({});
exitModalState (0);
}
@@ -294,21 +315,40 @@ private:
//==============================================================================
static void didPickDocumentAtURL (id self, SEL, UIDocumentPickerViewController*, NSURL* url)
{
auto picker = getOwner (self);
if (picker != nullptr)
if (auto* picker = getOwner (self))
picker->didPickDocumentAtURL (url);
}
static void documentPickerWasCancelled (id self, SEL, UIDocumentPickerViewController*)
{
auto picker = getOwner (self);
if (picker != nullptr)
if (auto* picker = getOwner (self))
picker->pickerWasCancelled();
}
};
struct FileChooserControllerClass : public ObjCClass<UIDocumentPickerViewController>
{
FileChooserControllerClass() : ObjCClass<UIDocumentPickerViewController> ("FileChooserController_")
{
addIvar<Native*> ("owner");
addMethod (@selector (viewDidDisappear:), viewDidDisappear, "v@:@c");
registerClass();
}
static void setOwner (id self, Native* owner) { object_setInstanceVariable (self, "owner", owner); }
static Native* getOwner (id self) { return getIvar<Native*> (self, "owner"); }
//==============================================================================
static void viewDidDisappear (id self, SEL, BOOL animated)
{
sendSuperclassMessage<void> (self, @selector (viewDidDisappear:), animated);
if (auto* picker = getOwner (self))
picker->triggerAsyncUpdate();
}
};
//==============================================================================
FileChooser& owner;
std::unique_ptr<NSObject<UIDocumentPickerDelegate>, NSObjectDeleter> delegate;
@@ -316,6 +356,7 @@ private:
UIViewComponentPeer* peer = nullptr;
static FileChooserDelegateClass fileChooserDelegateClass;
static FileChooserControllerClass fileChooserControllerClass;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Native)


+ 134
- 261
source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm View File

@@ -28,14 +28,6 @@ namespace juce
class UIViewComponentPeer;
// The way rotation works changed in iOS8..
static bool isUsingOldRotationMethod() noexcept
{
static bool isPreV8 = ([[[UIDevice currentDevice] systemVersion] compare: @"8.0"
options: NSNumericSearch] == NSOrderedAscending);
return isPreV8;
}
static UIInterfaceOrientation getWindowOrientation()
{
UIApplication* sharedApplication = [UIApplication sharedApplication];
@@ -79,28 +71,11 @@ namespace Orientations
return UIInterfaceOrientationPortrait;
}
static CGAffineTransform getCGTransformFor (const Desktop::DisplayOrientation orientation) noexcept
{
if (isUsingOldRotationMethod())
{
switch (orientation)
{
case Desktop::upsideDown: return CGAffineTransformMake (-1, 0, 0, -1, 0, 0);
case Desktop::rotatedClockwise: return CGAffineTransformMake (0, -1, 1, 0, 0, 0);
case Desktop::rotatedAntiClockwise: return CGAffineTransformMake (0, 1, -1, 0, 0, 0);
case Desktop::upright:
case Desktop::allOrientations:
default: break;
}
}
return CGAffineTransformIdentity;
}
static NSUInteger getSupportedOrientations()
{
NSUInteger allowed = 0;
Desktop& d = Desktop::getInstance();
auto& d = Desktop::getInstance();
if (d.isOrientationEnabled (Desktop::upright)) allowed |= UIInterfaceOrientationMaskPortrait;
if (d.isOrientationEnabled (Desktop::upsideDown)) allowed |= UIInterfaceOrientationMaskPortraitUpsideDown;
@@ -200,12 +175,10 @@ namespace juce
struct UIViewPeerControllerReceiver
{
virtual ~UIViewPeerControllerReceiver();
virtual ~UIViewPeerControllerReceiver() = default;
virtual void setViewController (UIViewController*) = 0;
};
UIViewPeerControllerReceiver::~UIViewPeerControllerReceiver() {}
class UIViewComponentPeer : public ComponentPeer,
public FocusChangeListener,
public UIViewPeerControllerReceiver
@@ -259,7 +232,7 @@ public:
void updateHiddenTextContent (TextInputTarget*);
void globalFocusChanged (Component*) override;
void updateTransformAndScreenBounds();
void updateScreenBounds();
void handleTouches (UIEvent*, bool isDown, bool isUp, bool isCancel);
@@ -268,10 +241,11 @@ public:
void performAnyPendingRepaintsNow() override;
//==============================================================================
UIWindow* window;
JuceUIView* view;
UIViewController* controller;
bool isSharedWindow, fullScreen, insideDrawRect, isAppex;
UIWindow* window = nil;
JuceUIView* view = nil;
UIViewController* controller = nil;
const bool isSharedWindow, isAppex;
bool fullScreen = false, insideDrawRect = false;
static int64 getMouseTime (UIEvent* e) noexcept
{
@@ -279,73 +253,10 @@ public:
+ (int64) ([e timestamp] * 1000.0);
}
static Rectangle<int> rotatedScreenPosToReal (const Rectangle<int>& r)
{
if (! SystemStats::isRunningInAppExtensionSandbox() && isUsingOldRotationMethod())
{
const Rectangle<int> screen (convertToRectInt ([UIScreen mainScreen].bounds));
switch (getWindowOrientation())
{
case UIInterfaceOrientationPortrait:
return r;
case UIInterfaceOrientationPortraitUpsideDown:
return Rectangle<int> (screen.getWidth() - r.getRight(), screen.getHeight() - r.getBottom(),
r.getWidth(), r.getHeight());
case UIInterfaceOrientationLandscapeLeft:
return Rectangle<int> (r.getY(), screen.getHeight() - r.getRight(),
r.getHeight(), r.getWidth());
case UIInterfaceOrientationLandscapeRight:
return Rectangle<int> (screen.getWidth() - r.getBottom(), r.getX(),
r.getHeight(), r.getWidth());
case UIInterfaceOrientationUnknown:
default: jassertfalse; // unknown orientation!
}
}
return r;
}
static Rectangle<int> realScreenPosToRotated (const Rectangle<int>& r)
{
if (! SystemStats::isRunningInAppExtensionSandbox() && isUsingOldRotationMethod())
{
const Rectangle<int> screen (convertToRectInt ([UIScreen mainScreen].bounds));
switch (getWindowOrientation())
{
case UIInterfaceOrientationPortrait:
return r;
case UIInterfaceOrientationPortraitUpsideDown:
return Rectangle<int> (screen.getWidth() - r.getRight(), screen.getHeight() - r.getBottom(),
r.getWidth(), r.getHeight());
case UIInterfaceOrientationLandscapeLeft:
return Rectangle<int> (screen.getHeight() - r.getBottom(), r.getX(),
r.getHeight(), r.getWidth());
case UIInterfaceOrientationLandscapeRight:
return Rectangle<int> (r.getY(), screen.getWidth() - r.getRight(),
r.getHeight(), r.getWidth());
case UIInterfaceOrientationUnknown:
default: jassertfalse; // unknown orientation!
}
}
return r;
}
static MultiTouchMapper<UITouch*> currentTouches;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponentPeer)
//==============================================================================
class AsyncRepaintMessage : public CallbackMessage
{
public:
@@ -363,6 +274,9 @@ private:
peer->repaint (rect);
}
};
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponentPeer)
};
static void sendScreenBoundsUpdate (JuceUIViewController* c)
@@ -370,7 +284,7 @@ static void sendScreenBoundsUpdate (JuceUIViewController* c)
JuceUIView* juceView = (JuceUIView*) [c view];
if (juceView != nil && juceView->owner != nullptr)
juceView->owner->updateTransformAndScreenBounds();
juceView->owner->updateScreenBounds();
}
void AsyncBoundsUpdater::handleAsyncUpdate()
@@ -614,14 +528,9 @@ bool KeyPress::isKeyCurrentlyDown (int)
Point<float> juce_lastMousePos;
//==============================================================================
UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyleFlags, UIView* viewToAttachTo)
UIViewComponentPeer::UIViewComponentPeer (Component& comp, int windowStyleFlags, UIView* viewToAttachTo)
: ComponentPeer (comp, windowStyleFlags),
window (nil),
view (nil),
controller (nil),
isSharedWindow (viewToAttachTo != nil),
fullScreen (false),
insideDrawRect (false),
isAppex (SystemStats::isRunningInAppExtensionSandbox())
{
CGRect r = convertToCGRect (component.getBounds());
@@ -632,7 +541,6 @@ UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyle
view.hidden = true;
view.opaque = component.isOpaque();
view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent: 0];
view.transform = CGAffineTransformIdentity;
if (isSharedWindow)
{
@@ -641,7 +549,7 @@ UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyle
}
else
{
r = convertToCGRect (rotatedScreenPosToReal (component.getBounds()));
r = convertToCGRect (component.getBounds());
r.origin.y = [UIScreen mainScreen].bounds.size.height - (r.origin.y + r.size.height);
window = [[JuceUIWindow alloc] initWithFrame: r];
@@ -652,7 +560,6 @@ UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyle
window.rootViewController = controller;
window.hidden = true;
window.transform = Orientations::getCGTransformFor (Desktop::getInstance().getCurrentOrientation());
window.opaque = component.isOpaque();
window.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent: 0];
@@ -660,8 +567,6 @@ UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyle
window.windowLevel = UIWindowLevelAlert;
view.frame = CGRectMake (0, 0, r.size.width, r.size.height);
[window addSubview: view];
}
setTitle (component.getName());
@@ -718,7 +623,7 @@ void UIViewComponentPeer::setBounds (const Rectangle<int>& newBounds, const bool
}
else
{
window.frame = convertToCGRect (rotatedScreenPosToReal (newBounds));
window.frame = convertToCGRect (newBounds);
view.frame = CGRectMake (0, 0, (CGFloat) newBounds.getWidth(), (CGFloat) newBounds.getHeight());
handleMovedOrResized();
@@ -733,8 +638,6 @@ Rectangle<int> UIViewComponentPeer::getBounds (const bool global) const
{
r = [view convertRect: r toView: view.window];
r = [view.window convertRect: r toWindow: nil];
return realScreenPosToRotated (convertToRectInt (r));
}
return convertToRectInt (r);
@@ -759,8 +662,8 @@ void UIViewComponentPeer::setFullScreen (bool shouldBeFullScreen)
{
if (! isSharedWindow)
{
Rectangle<int> r (shouldBeFullScreen ? Desktop::getInstance().getDisplays().getMainDisplay().userArea
: lastNonFullscreenBounds);
auto r = shouldBeFullScreen ? Desktop::getInstance().getDisplays().getMainDisplay().userArea
: lastNonFullscreenBounds;
if ((! shouldBeFullScreen) && r.isEmpty())
r = getBounds();
@@ -773,16 +676,14 @@ void UIViewComponentPeer::setFullScreen (bool shouldBeFullScreen)
}
}
void UIViewComponentPeer::updateTransformAndScreenBounds()
void UIViewComponentPeer::updateScreenBounds()
{
Desktop& desktop = Desktop::getInstance();
const Rectangle<int> oldArea (component.getBounds());
const Rectangle<int> oldDesktop (desktop.getDisplays().getMainDisplay().userArea);
auto& desktop = Desktop::getInstance();
const_cast<Displays&> (desktop.getDisplays()).refresh();
auto oldArea = component.getBounds();
auto oldDesktop = desktop.getDisplays().getMainDisplay().userArea;
window.transform = Orientations::getCGTransformFor (desktop.getCurrentOrientation());
view.transform = CGAffineTransformIdentity;
const_cast<Displays&> (desktop.getDisplays()).refresh();
if (fullScreen)
{
@@ -792,13 +693,13 @@ void UIViewComponentPeer::updateTransformAndScreenBounds()
else if (! isSharedWindow)
{
// this will re-centre the window, but leave its size unchanged
const float centreRelX = oldArea.getCentreX() / (float) oldDesktop.getWidth();
const float centreRelY = oldArea.getCentreY() / (float) oldDesktop.getHeight();
auto centreRelX = oldArea.getCentreX() / (float) oldDesktop.getWidth();
auto centreRelY = oldArea.getCentreY() / (float) oldDesktop.getHeight();
const Rectangle<int> newDesktop (desktop.getDisplays().getMainDisplay().userArea);
auto newDesktop = desktop.getDisplays().getMainDisplay().userArea;
const int x = ((int) (newDesktop.getWidth() * centreRelX)) - (oldArea.getWidth() / 2);
const int y = ((int) (newDesktop.getHeight() * centreRelY)) - (oldArea.getHeight() / 2);
auto x = ((int) (newDesktop.getWidth() * centreRelX)) - (oldArea.getWidth() / 2);
auto y = ((int) (newDesktop.getHeight() * centreRelY)) - (oldArea.getHeight() / 2);
component.setBounds (oldArea.withPosition (x, y));
}
@@ -808,13 +709,8 @@ void UIViewComponentPeer::updateTransformAndScreenBounds()
bool UIViewComponentPeer::contains (Point<int> localPos, bool trueIfInAChildWindow) const
{
{
Rectangle<int> localBounds =
ScalingHelpers::scaledScreenPosToUnscaled (component, component.getLocalBounds());
if (! localBounds.contains (localPos))
return false;
}
if (! ScalingHelpers::scaledScreenPosToUnscaled (component, component.getLocalBounds()).contains (localPos))
return false;
UIView* v = [view hitTest: convertToCGPoint (localPos)
withEvent: nil];
@@ -844,16 +740,10 @@ void UIViewComponentPeer::toFront (bool makeActiveWindow)
void UIViewComponentPeer::toBehind (ComponentPeer* other)
{
if (UIViewComponentPeer* const otherPeer = dynamic_cast<UIViewComponentPeer*> (other))
if (auto* otherPeer = dynamic_cast<UIViewComponentPeer*> (other))
{
if (isSharedWindow)
{
[[view superview] insertSubview: view belowSubview: otherPeer->view];
}
else
{
// don't know how to do this
}
}
else
{
@@ -869,23 +759,17 @@ void UIViewComponentPeer::setIcon (const Image& /*newIcon*/)
//==============================================================================
static float getMaximumTouchForce (UITouch* touch) noexcept
{
#if defined (__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
if ([touch respondsToSelector: @selector (maximumPossibleForce)])
return (float) touch.maximumPossibleForce;
#endif
ignoreUnused (touch);
return 0.0f;
}
static float getTouchForce (UITouch* touch) noexcept
{
#if defined (__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
if ([touch respondsToSelector: @selector (force)])
return (float) touch.force;
#endif
ignoreUnused (touch);
return 0.0f;
}
@@ -896,19 +780,19 @@ void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, cons
for (unsigned int i = 0; i < [touches count]; ++i)
{
UITouch* touch = [touches objectAtIndex: i];
const float maximumForce = getMaximumTouchForce (touch);
auto maximumForce = getMaximumTouchForce (touch);
if ([touch phase] == UITouchPhaseStationary && maximumForce <= 0)
continue;
CGPoint p = [touch locationInView: view];
const Point<float> pos (static_cast<float> (p.x), static_cast<float> (p.y));
Point<float> pos ((float) p.x, (float) p.y);
juce_lastMousePos = pos + getBounds (true).getPosition().toFloat();
const int64 time = getMouseTime (event);
const int touchIndex = currentTouches.getIndexOfTouch (this, touch);
auto time = getMouseTime (event);
auto touchIndex = currentTouches.getIndexOfTouch (this, touch);
ModifierKeys modsToSend (ModifierKeys::currentModifiers);
auto modsToSend = ModifierKeys::currentModifiers;
if (isDown)
{
@@ -944,8 +828,8 @@ void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, cons
}
// NB: some devices return 0 or 1.0 if pressure is unknown, so we'll clip our value to a believable range:
float pressure = maximumForce > 0 ? jlimit (0.0001f, 0.9999f, getTouchForce (touch) / maximumForce)
: MouseInputSource::invalidPressure;
auto pressure = maximumForce > 0 ? jlimit (0.0001f, 0.9999f, getTouchForce (touch) / maximumForce)
: MouseInputSource::invalidPressure;
handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, modsToSend, pressure,
MouseInputSource::invalidOrientation, time, { }, touchIndex);
@@ -1011,18 +895,13 @@ void UIViewComponentPeer::textInputRequired (Point<int>, TextInputTarget&)
{
}
static bool isIOS4_1() noexcept
{
return [[[UIDevice currentDevice] systemVersion] doubleValue] >= 4.1;
}
static UIKeyboardType getUIKeyboardType (TextInputTarget::VirtualKeyboardType type) noexcept
{
switch (type)
{
case TextInputTarget::textKeyboard: return UIKeyboardTypeAlphabet;
case TextInputTarget::numericKeyboard: return isIOS4_1() ? UIKeyboardTypeNumberPad : UIKeyboardTypeNumbersAndPunctuation;
case TextInputTarget::decimalKeyboard: return isIOS4_1() ? UIKeyboardTypeDecimalPad : UIKeyboardTypeNumbersAndPunctuation;
case TextInputTarget::numericKeyboard: return UIKeyboardTypeNumbersAndPunctuation;
case TextInputTarget::decimalKeyboard: return UIKeyboardTypeNumbersAndPunctuation;
case TextInputTarget::urlKeyboard: return UIKeyboardTypeURL;
case TextInputTarget::emailAddressKeyboard: return UIKeyboardTypeEmailAddress;
case TextInputTarget::phoneNumberKeyboard: return UIKeyboardTypePhonePad;
@@ -1041,9 +920,9 @@ void UIViewComponentPeer::updateHiddenTextContent (TextInputTarget* target)
BOOL UIViewComponentPeer::textViewReplaceCharacters (Range<int> range, const String& text)
{
if (TextInputTarget* const target = findCurrentTextInputTarget())
if (auto* target = findCurrentTextInputTarget())
{
const Range<int> currentSelection (target->getHighlightedRegion());
auto currentSelection = target->getHighlightedRegion();
if (range.getLength() == 1 && text.isEmpty()) // (detect backspace)
if (currentSelection.isEmpty())
@@ -1062,15 +941,16 @@ BOOL UIViewComponentPeer::textViewReplaceCharacters (Range<int> range, const Str
void UIViewComponentPeer::globalFocusChanged (Component*)
{
if (TextInputTarget* const target = findCurrentTextInputTarget())
if (auto* target = findCurrentTextInputTarget())
{
Component* comp = dynamic_cast<Component*> (target);
Point<int> pos (component.getLocalPoint (comp, Point<int>()));
view->hiddenTextView.frame = CGRectMake (pos.x, pos.y, 0, 0);
if (auto* comp = dynamic_cast<Component*> (target))
{
auto pos = component.getLocalPoint (comp, Point<int>());
view->hiddenTextView.frame = CGRectMake (pos.x, pos.y, 0, 0);
updateHiddenTextContent (target);
[view->hiddenTextView becomeFirstResponder];
updateHiddenTextContent (target);
[view->hiddenTextView becomeFirstResponder];
}
}
else
{
@@ -1078,7 +958,6 @@ void UIViewComponentPeer::globalFocusChanged (Component*)
}
}
//==============================================================================
void UIViewComponentPeer::drawRect (CGRect r)
{
@@ -1091,9 +970,6 @@ void UIViewComponentPeer::drawRect (CGRect r)
CGContextClearRect (cg, CGContextGetClipBoundingBox (cg));
CGContextConcatCTM (cg, CGAffineTransformMake (1, 0, 0, -1, 0, getComponent().getHeight()));
// NB the CTM on iOS already includes a factor for the display scale, so
// we'll tell the context that the scale is 1.0 to avoid it using it twice
CoreGraphicsContext g (cg, getComponent().getHeight());
insideDrawRect = true;
@@ -1111,9 +987,9 @@ void Desktop::setKioskComponent (Component* kioskModeComp, bool enableOrDisable,
{
displays->refresh();
if (ComponentPeer* peer = kioskModeComp->getPeer())
if (auto* peer = kioskModeComp->getPeer())
{
if (UIViewComponentPeer* uiViewPeer = dynamic_cast<UIViewComponentPeer*> (peer))
if (auto* uiViewPeer = dynamic_cast<UIViewComponentPeer*> (peer))
[uiViewPeer->controller setNeedsStatusBarAppearanceUpdate];
peer->setFullScreen (enableOrDisable);
@@ -1125,21 +1001,18 @@ void Desktop::allowedOrientationsChanged()
// if the current orientation isn't allowed anymore then switch orientations
if (! isOrientationEnabled (getCurrentOrientation()))
{
DisplayOrientation orientations[] = { upright, upsideDown, rotatedClockwise, rotatedAntiClockwise };
const int n = sizeof (orientations) / sizeof (DisplayOrientation);
int i;
for (i = 0; i < n; ++i)
if (isOrientationEnabled (orientations[i]))
break;
auto newOrientation = [this]
{
for (auto orientation : { upright, upsideDown, rotatedClockwise, rotatedAntiClockwise })
if (isOrientationEnabled (orientation))
return orientation;
// you need to support at least one orientation
jassert (i < n);
i = jmin (n - 1, i);
// you need to support at least one orientation
jassertfalse;
return upright;
}();
NSNumber *value = [NSNumber numberWithInt: (int) Orientations::convertFromJuce (orientations[i])];
NSNumber* value = [NSNumber numberWithInt: (int) Orientations::convertFromJuce (newOrientation)];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
[value release];
}
@@ -1164,77 +1037,77 @@ ComponentPeer* Component::createNewPeer (int styleFlags, void* windowToAttachTo)
}
//==============================================================================
const int KeyPress::spaceKey = ' ';
const int KeyPress::returnKey = 0x0d;
const int KeyPress::escapeKey = 0x1b;
const int KeyPress::backspaceKey = 0x7f;
const int KeyPress::leftKey = 0x1000;
const int KeyPress::rightKey = 0x1001;
const int KeyPress::upKey = 0x1002;
const int KeyPress::downKey = 0x1003;
const int KeyPress::pageUpKey = 0x1004;
const int KeyPress::pageDownKey = 0x1005;
const int KeyPress::endKey = 0x1006;
const int KeyPress::homeKey = 0x1007;
const int KeyPress::deleteKey = 0x1008;
const int KeyPress::insertKey = -1;
const int KeyPress::tabKey = 9;
const int KeyPress::F1Key = 0x2001;
const int KeyPress::F2Key = 0x2002;
const int KeyPress::F3Key = 0x2003;
const int KeyPress::F4Key = 0x2004;
const int KeyPress::F5Key = 0x2005;
const int KeyPress::F6Key = 0x2006;
const int KeyPress::F7Key = 0x2007;
const int KeyPress::F8Key = 0x2008;
const int KeyPress::F9Key = 0x2009;
const int KeyPress::F10Key = 0x200a;
const int KeyPress::F11Key = 0x200b;
const int KeyPress::F12Key = 0x200c;
const int KeyPress::F13Key = 0x200d;
const int KeyPress::F14Key = 0x200e;
const int KeyPress::F15Key = 0x200f;
const int KeyPress::F16Key = 0x2010;
const int KeyPress::F17Key = 0x2011;
const int KeyPress::F18Key = 0x2012;
const int KeyPress::F19Key = 0x2013;
const int KeyPress::F20Key = 0x2014;
const int KeyPress::F21Key = 0x2015;
const int KeyPress::F22Key = 0x2016;
const int KeyPress::F23Key = 0x2017;
const int KeyPress::F24Key = 0x2018;
const int KeyPress::F25Key = 0x2019;
const int KeyPress::F26Key = 0x201a;
const int KeyPress::F27Key = 0x201b;
const int KeyPress::F28Key = 0x201c;
const int KeyPress::F29Key = 0x201d;
const int KeyPress::F30Key = 0x201e;
const int KeyPress::F31Key = 0x201f;
const int KeyPress::F32Key = 0x2020;
const int KeyPress::F33Key = 0x2021;
const int KeyPress::F34Key = 0x2022;
const int KeyPress::F35Key = 0x2023;
const int KeyPress::numberPad0 = 0x30020;
const int KeyPress::numberPad1 = 0x30021;
const int KeyPress::numberPad2 = 0x30022;
const int KeyPress::numberPad3 = 0x30023;
const int KeyPress::numberPad4 = 0x30024;
const int KeyPress::numberPad5 = 0x30025;
const int KeyPress::numberPad6 = 0x30026;
const int KeyPress::numberPad7 = 0x30027;
const int KeyPress::numberPad8 = 0x30028;
const int KeyPress::numberPad9 = 0x30029;
const int KeyPress::numberPadAdd = 0x3002a;
const int KeyPress::numberPadSubtract = 0x3002b;
const int KeyPress::numberPadMultiply = 0x3002c;
const int KeyPress::numberPadDivide = 0x3002d;
const int KeyPress::numberPadSeparator = 0x3002e;
const int KeyPress::numberPadDecimalPoint = 0x3002f;
const int KeyPress::numberPadEquals = 0x30030;
const int KeyPress::numberPadDelete = 0x30031;
const int KeyPress::playKey = 0x30000;
const int KeyPress::stopKey = 0x30001;
const int KeyPress::fastForwardKey = 0x30002;
const int KeyPress::rewindKey = 0x30003;
const int KeyPress::spaceKey = ' ';
const int KeyPress::returnKey = 0x0d;
const int KeyPress::escapeKey = 0x1b;
const int KeyPress::backspaceKey = 0x7f;
const int KeyPress::leftKey = 0x1000;
const int KeyPress::rightKey = 0x1001;
const int KeyPress::upKey = 0x1002;
const int KeyPress::downKey = 0x1003;
const int KeyPress::pageUpKey = 0x1004;
const int KeyPress::pageDownKey = 0x1005;
const int KeyPress::endKey = 0x1006;
const int KeyPress::homeKey = 0x1007;
const int KeyPress::deleteKey = 0x1008;
const int KeyPress::insertKey = -1;
const int KeyPress::tabKey = 9;
const int KeyPress::F1Key = 0x2001;
const int KeyPress::F2Key = 0x2002;
const int KeyPress::F3Key = 0x2003;
const int KeyPress::F4Key = 0x2004;
const int KeyPress::F5Key = 0x2005;
const int KeyPress::F6Key = 0x2006;
const int KeyPress::F7Key = 0x2007;
const int KeyPress::F8Key = 0x2008;
const int KeyPress::F9Key = 0x2009;
const int KeyPress::F10Key = 0x200a;
const int KeyPress::F11Key = 0x200b;
const int KeyPress::F12Key = 0x200c;
const int KeyPress::F13Key = 0x200d;
const int KeyPress::F14Key = 0x200e;
const int KeyPress::F15Key = 0x200f;
const int KeyPress::F16Key = 0x2010;
const int KeyPress::F17Key = 0x2011;
const int KeyPress::F18Key = 0x2012;
const int KeyPress::F19Key = 0x2013;
const int KeyPress::F20Key = 0x2014;
const int KeyPress::F21Key = 0x2015;
const int KeyPress::F22Key = 0x2016;
const int KeyPress::F23Key = 0x2017;
const int KeyPress::F24Key = 0x2018;
const int KeyPress::F25Key = 0x2019;
const int KeyPress::F26Key = 0x201a;
const int KeyPress::F27Key = 0x201b;
const int KeyPress::F28Key = 0x201c;
const int KeyPress::F29Key = 0x201d;
const int KeyPress::F30Key = 0x201e;
const int KeyPress::F31Key = 0x201f;
const int KeyPress::F32Key = 0x2020;
const int KeyPress::F33Key = 0x2021;
const int KeyPress::F34Key = 0x2022;
const int KeyPress::F35Key = 0x2023;
const int KeyPress::numberPad0 = 0x30020;
const int KeyPress::numberPad1 = 0x30021;
const int KeyPress::numberPad2 = 0x30022;
const int KeyPress::numberPad3 = 0x30023;
const int KeyPress::numberPad4 = 0x30024;
const int KeyPress::numberPad5 = 0x30025;
const int KeyPress::numberPad6 = 0x30026;
const int KeyPress::numberPad7 = 0x30027;
const int KeyPress::numberPad8 = 0x30028;
const int KeyPress::numberPad9 = 0x30029;
const int KeyPress::numberPadAdd = 0x3002a;
const int KeyPress::numberPadSubtract = 0x3002b;
const int KeyPress::numberPadMultiply = 0x3002c;
const int KeyPress::numberPadDivide = 0x3002d;
const int KeyPress::numberPadSeparator = 0x3002e;
const int KeyPress::numberPadDecimalPoint = 0x3002f;
const int KeyPress::numberPadEquals = 0x30030;
const int KeyPress::numberPadDelete = 0x30031;
const int KeyPress::playKey = 0x30000;
const int KeyPress::stopKey = 0x30001;
const int KeyPress::fastForwardKey = 0x30002;
const int KeyPress::rewindKey = 0x30003;
} // namespace juce

+ 3
- 81
source/modules/juce_gui_basics/native/juce_ios_Windowing.mm View File

@@ -29,7 +29,7 @@ namespace juce
struct AppInactivityCallback // NB: careful, this declaration is duplicated in other modules
{
virtual ~AppInactivityCallback() {}
virtual ~AppInactivityCallback() = default;
virtual void appBecomingInactive() = 0;
};
@@ -434,30 +434,6 @@ void LookAndFeel::playAlertSound()
}
//==============================================================================
class iOSMessageBox;
#if defined (__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
#define JUCE_USE_NEW_IOS_ALERTWINDOW 1
#endif
#if ! JUCE_USE_NEW_IOS_ALERTWINDOW
} // (juce namespace)
@interface JuceAlertBoxDelegate : NSObject <UIAlertViewDelegate>
{
@public
iOSMessageBox* owner;
}
- (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex;
@end
namespace juce
{
#endif
class iOSMessageBox
{
public:
@@ -466,7 +442,6 @@ public:
ModalComponentManager::Callback* cb, const bool async)
: result (0), resultReceived (false), callback (cb), isAsync (async)
{
#if JUCE_USE_NEW_IOS_ALERTWINDOW
if (currentlyFocusedPeer != nullptr)
{
UIAlertController* alert = [UIAlertController alertControllerWithTitle: juceStringToNS (title)
@@ -486,27 +461,6 @@ public:
// have at least one window on screen when you use this
jassertfalse;
}
#else
delegate = [[JuceAlertBoxDelegate alloc] init];
delegate->owner = this;
alert = [[UIAlertView alloc] initWithTitle: juceStringToNS (title)
message: juceStringToNS (message)
delegate: delegate
cancelButtonTitle: button1
otherButtonTitles: button2, button3, nil];
[alert retain];
[alert show];
#endif
}
~iOSMessageBox()
{
#if ! JUCE_USE_NEW_IOS_ALERTWINDOW
[alert release];
[delegate release];
#endif
}
int getResult()
@@ -515,11 +469,7 @@ public:
JUCE_AUTORELEASEPOOL
{
#if JUCE_USE_NEW_IOS_ALERTWINDOW
while (! resultReceived)
#else
while (! (alert.hidden || resultReceived))
#endif
[[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
}
@@ -544,7 +494,6 @@ private:
std::unique_ptr<ModalComponentManager::Callback> callback;
const bool isAsync;
#if JUCE_USE_NEW_IOS_ALERTWINDOW
void addButton (UIAlertController* alert, NSString* text, int index)
{
if (text != nil)
@@ -552,33 +501,10 @@ private:
style: UIAlertActionStyleDefault
handler: ^(UIAlertAction*) { this->buttonClicked (index); }]];
}
#else
UIAlertView* alert;
JuceAlertBoxDelegate* delegate;
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSMessageBox)
};
#if ! JUCE_USE_NEW_IOS_ALERTWINDOW
} // (juce namespace)
@implementation JuceAlertBoxDelegate
- (void) alertView: (UIAlertView*) alertView clickedButtonAtIndex: (NSInteger) buttonIndex
{
owner->buttonClicked ((int) buttonIndex);
alertView.hidden = true;
}
@end
namespace juce
{
#endif
//==============================================================================
#if JUCE_MODAL_LOOPS_PERMITTED
void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType /*iconType*/,
@@ -742,13 +668,9 @@ void Displays::findDisplays (float masterScale)
UIScreen* s = [UIScreen mainScreen];
Display d;
d.userArea = d.totalArea = UIViewComponentPeer::realScreenPosToRotated (convertToRectInt ([s bounds])) / masterScale;
d.userArea = d.totalArea = convertToRectInt ([s bounds]) / masterScale;
d.isMain = true;
d.scale = masterScale;
if ([s respondsToSelector: @selector (scale)])
d.scale *= s.scale;
d.scale = masterScale * s.scale;
d.dpi = 160 * d.scale;
displays.add (d);


+ 11
- 10
source/modules/juce_gui_basics/native/juce_linux_FileChooser.cpp View File

@@ -27,16 +27,18 @@ namespace juce
{
#if JUCE_MODAL_LOOPS_PERMITTED
static bool exeIsAvailable (const char* const executable)
static bool exeIsAvailable (String executable)
{
ChildProcess child;
const bool ok = child.start ("which " + String (executable))
&& child.readAllProcessOutput().trim().isNotEmpty();
child.waitForProcessToFinish (60 * 1000);
return ok;
}
if (child.start ("which " + executable))
{
child.waitForProcessToFinish (60 * 1000);
return (child.getExitCode() == 0);
}
return false;
}
class FileChooser::Native : public FileChooser::Pimpl,
private Timer
@@ -68,7 +70,7 @@ public:
child.start (args, ChildProcess::wantStdOut);
while (child.isRunning())
if (! MessageManager::getInstance()->runDispatchLoopUntil(20))
if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
break;
finish (false);
@@ -187,7 +189,7 @@ private:
}
args.add (startPath.getFullPathName());
args.add (owner.filters.replaceCharacter (';', ' '));
args.add ("(" + owner.filters.replaceCharacter (';', ' ') + ")");
}
void addZenityArgs()
@@ -218,8 +220,7 @@ private:
StringArray tokens;
tokens.addTokens (owner.filters, ";,|", "\"");
for (int i = 0; i < tokens.size(); ++i)
args.add ("--file-filter=" + tokens[i]);
args.add ("--file-filter=" + tokens.joinIntoString (" "));
}
if (owner.startingFile.isDirectory())


+ 12
- 4
source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp View File

@@ -330,11 +330,17 @@ private:
class LinuxRepaintManager : public Timer
{
public:
LinuxRepaintManager (LinuxComponentPeer& p) : peer (p) {}
LinuxRepaintManager (LinuxComponentPeer& p)
: peer (p),
isSemiTransparentWindow ((peer.getStyleFlags() & ComponentPeer::windowIsSemiTransparent) != 0)
{
}
void timerCallback() override
{
if (XWindowSystem::getInstance()->getNumPaintsPending (peer.windowH) > 0)
XWindowSystem::getInstance()->processPendingPaintsForWindow (peer.windowH);
if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
return;
if (! regionsNeedingRepaint.isEmpty())
@@ -359,7 +365,7 @@ private:
void performAnyPendingRepaintsNow()
{
if (XWindowSystem::getInstance()->getNumPaintsPending (peer.windowH) > 0)
if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
{
startTimer (repaintTimerPeriod);
return;
@@ -374,7 +380,8 @@ private:
if (image.isNull() || image.getWidth() < totalArea.getWidth()
|| image.getHeight() < totalArea.getHeight())
{
image = XWindowSystem::getInstance()->createImage (totalArea.getWidth(), totalArea.getHeight(),
image = XWindowSystem::getInstance()->createImage (isSemiTransparentWindow,
totalArea.getWidth(), totalArea.getHeight(),
useARGBImagesForRendering);
}
@@ -407,6 +414,7 @@ private:
enum { repaintTimerPeriod = 1000 / 100 };
LinuxComponentPeer& peer;
const bool isSemiTransparentWindow;
Image image;
uint32 lastTimeImageUsed = 0;
RectangleList<int> regionsNeedingRepaint;


+ 1
- 1
source/modules/juce_gui_basics/native/juce_mac_FileChooser.mm View File

@@ -283,7 +283,7 @@ private:
//==============================================================================
struct DelegateClass : ObjCClass<DelegateType>
{
DelegateClass() : ObjCClass <DelegateType> ("JUCEFileChooser_")
DelegateClass() : ObjCClass<DelegateType> ("JUCEFileChooser_")
{
addIvar<Native*> ("cppObject");


+ 0
- 23
source/modules/juce_gui_basics/native/juce_mac_MainMenu.mm View File

@@ -534,30 +534,7 @@ private:
auto owner = getIvar<JuceMainMenuHandler*> (self, "owner");
if (auto* juceItem = getJuceClassFromNSObject<PopupMenu::Item> ([item representedObject]))
{
// If the menu is being triggered by a keypress, the OS will have picked it up before we had a chance to offer it to
// our own components, which may have wanted to intercept it. So, rather than dispatching directly, we'll feed it back
// into the focused component and let it trigger the menu item indirectly.
NSEvent* e = [NSApp currentEvent];
if ([e type] == NSEventTypeKeyDown || [e type] == NSEventTypeKeyUp)
{
if (auto* focused = juce::Component::getCurrentlyFocusedComponent())
{
if (auto peer = dynamic_cast<juce::NSViewComponentPeer*> (focused->getPeer()))
{
if ([e type] == NSEventTypeKeyDown)
peer->redirectKeyDown (e);
else
peer->redirectKeyUp (e);
return;
}
}
}
owner->invoke (*juceItem, static_cast<int> ([item tag]));
}
}
static void menuNeedsUpdate (id self, SEL, NSMenu* menu)


+ 21
- 17
source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm View File

@@ -23,6 +23,11 @@
==============================================================================
*/
@interface NSEvent (DeviceDelta)
- (float)deviceDeltaX;
- (float)deviceDeltaY;
@end
//==============================================================================
#if defined (MAC_OS_X_VERSION_10_8) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8) \
&& USE_COREGRAPHICS_RENDERING && JUCE_COREGRAPHICS_DRAW_ASYNC
@@ -680,8 +685,8 @@ public:
}
else if ([ev respondsToSelector: @selector (deviceDeltaX)])
{
wheel.deltaX = checkDeviceDeltaReturnValue ((float) getMsgSendFPRetFn() (ev, @selector (deviceDeltaX)));
wheel.deltaY = checkDeviceDeltaReturnValue ((float) getMsgSendFPRetFn() (ev, @selector (deviceDeltaY)));
wheel.deltaX = checkDeviceDeltaReturnValue ([ev deviceDeltaX]);
wheel.deltaY = checkDeviceDeltaReturnValue ([ev deviceDeltaY]);
}
}
@catch (...)
@@ -1770,10 +1775,7 @@ private:
owner->stringBeingComposed.clear();
if (! (owner->textWasInserted || owner->redirectKeyDown (ev)))
{
objc_super s = { self, [NSView class] };
getMsgSendSuperFn() (&s, @selector (keyDown:), ev);
}
sendSuperclassMessage<void> (self, @selector (keyDown:), ev);
}
}
@@ -1781,11 +1783,8 @@ private:
{
auto* owner = getOwner (self);
if (owner == nullptr || ! owner->redirectKeyUp (ev))
{
objc_super s = { self, [NSView class] };
getMsgSendSuperFn() (&s, @selector (keyUp:), ev);
}
if (! owner->redirectKeyUp (ev))
sendSuperclassMessage<void> (self, @selector (keyUp:), ev);
}
//==============================================================================
@@ -2035,7 +2034,7 @@ private:
static void becomeKeyWindow (id self, SEL)
{
sendSuperclassMessage (self, @selector (becomeKeyWindow));
sendSuperclassMessage<void> (self, @selector (becomeKeyWindow));
if (auto* owner = getOwner (self))
{
@@ -2057,10 +2056,15 @@ private:
return owner == nullptr || owner->windowShouldClose();
}
static NSRect constrainFrameRect (id self, SEL, NSRect frameRect, NSScreen*)
static NSRect constrainFrameRect (id self, SEL, NSRect frameRect, NSScreen* screen)
{
if (auto* owner = getOwner (self))
{
frameRect = sendSuperclassMessage<NSRect, NSRect, NSScreen*> (self, @selector (constrainFrameRect:toScreen:),
frameRect, screen);
frameRect = owner->constrainRect (frameRect);
}
return frameRect;
}
@@ -2104,10 +2108,10 @@ private:
{
if (auto* owner = getOwner (self))
{
owner->isZooming = true;
objc_super s = { self, [NSWindow class] };
getMsgSendSuperFn() (&s, @selector (zoom:), sender);
owner->isZooming = false;
{
const ScopedValueSetter<bool> svs (owner->isZooming, true);
sendSuperclassMessage<void> (self, @selector (zoom:), sender);
}
owner->redirectMovedOrResized();
}


+ 1
- 1
source/modules/juce_gui_basics/native/juce_mac_Windowing.mm View File

@@ -217,7 +217,7 @@ private:
delete getIvar<std::function<void()>*> (self, "callback");
delete getIvar<NSDragOperation*> (self, "operation");
sendSuperclassMessage (self, @selector (dealloc));
sendSuperclassMessage<void> (self, @selector (dealloc));
}
static void provideDataForType (id self, SEL, NSPasteboard* sender, NSPasteboardItem*, NSString* type)


+ 182
- 20
source/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp View File

@@ -58,7 +58,7 @@ public:
// Handle nonexistent root directories in the same way as existing ones
files.calloc (static_cast<size_t> (charsAvailableForResult) + 1);
if (startingFile.isDirectory() ||startingFile.isRoot())
if (startingFile.isDirectory() || startingFile.isRoot())
{
initialPath = startingFile.getFullPathName();
}
@@ -154,7 +154,7 @@ private:
Component::SafePointer<Component> owner;
String title, filtersString;
std::unique_ptr<CustomComponentHolder> customComponent;
String initialPath, returnedString, defaultExtension;
String initialPath, returnedString;
WaitableEvent threadHasReference;
CriticalSection deletingDialog;
@@ -167,8 +167,144 @@ private:
Atomic<HWND> nativeDialogRef;
Atomic<int> shouldCancel;
bool showDialog (IFileDialog& dialog, bool async) const
{
FILEOPENDIALOGOPTIONS flags = {};
if (FAILED (dialog.GetOptions (&flags)))
return false;
const auto setBit = [] (FILEOPENDIALOGOPTIONS& field, bool value, FILEOPENDIALOGOPTIONS option)
{
if (value)
field |= option;
else
field &= ~option;
};
setBit (flags, selectsDirectories, FOS_PICKFOLDERS);
setBit (flags, warnAboutOverwrite, FOS_OVERWRITEPROMPT);
setBit (flags, selectMultiple, FOS_ALLOWMULTISELECT);
setBit (flags, customComponent != nullptr, FOS_FORCEPREVIEWPANEON);
if (FAILED (dialog.SetOptions (flags)) || FAILED (dialog.SetTitle (title.toUTF16())))
return false;
PIDLIST_ABSOLUTE pidl = {};
if (FAILED (SHParseDisplayName (initialPath.toWideCharPointer(), nullptr, &pidl, SFGAO_FOLDER, nullptr)))
return false;
const auto item = [&]
{
ComSmartPtr<IShellItem> ptr;
SHCreateShellItem (nullptr, nullptr, pidl, ptr.resetAndGetPointerAddress());
return ptr;
}();
if (item == nullptr || FAILED (dialog.SetFolder (item)))
return false;
String filename (files.getData());
if (FAILED (dialog.SetFileName (filename.toWideCharPointer())))
return false;
auto extension = getDefaultFileExtension (filename);
if (extension.isNotEmpty() && FAILED (dialog.SetDefaultExtension (extension.toWideCharPointer())))
return false;
const COMDLG_FILTERSPEC spec[] { { filtersString.toWideCharPointer(), filtersString.toWideCharPointer() } };
if (! selectsDirectories && FAILED (dialog.SetFileTypes (numElementsInArray (spec), spec)))
return false;
return dialog.Show (static_cast<HWND> (async ? nullptr : owner->getWindowHandle())) == S_OK;
}
//==============================================================================
Array<URL> openDialog (bool async)
Array<URL> openDialogVistaAndUp (bool async)
{
const auto getUrl = [] (IShellItem& item)
{
struct Free
{
void operator() (LPWSTR ptr) const noexcept { CoTaskMemFree (ptr); }
};
LPWSTR ptr = nullptr;
item.GetDisplayName (SIGDN_URL, &ptr);
return std::unique_ptr<WCHAR, Free> { ptr };
};
if (isSave)
{
const auto dialog = [&]
{
ComSmartPtr<IFileDialog> ptr;
ptr.CoCreateInstance (CLSID_FileSaveDialog, CLSCTX_INPROC_SERVER);
return ptr;
}();
if (dialog == nullptr)
return {};
showDialog (*dialog, async);
const auto item = [&]
{
ComSmartPtr<IShellItem> ptr;
dialog->GetResult (ptr.resetAndGetPointerAddress());
return ptr;
}();
if (item == nullptr)
return {};
return { URL (String (getUrl (*item).get())) };
}
const auto dialog = [&]
{
ComSmartPtr<IFileOpenDialog> ptr;
ptr.CoCreateInstance (CLSID_FileOpenDialog, CLSCTX_INPROC_SERVER);
return ptr;
}();
if (dialog == nullptr)
return {};
showDialog (*dialog, async);
const auto items = [&]
{
ComSmartPtr<IShellItemArray> ptr;
dialog->GetResults (ptr.resetAndGetPointerAddress());
return ptr;
}();
if (items == nullptr)
return {};
Array<URL> result;
DWORD numItems = 0;
items->GetCount (&numItems);
for (DWORD i = 0; i < numItems; ++i)
{
ComSmartPtr<IShellItem> scope;
items->GetItemAt (i, scope.resetAndGetPointerAddress());
if (scope != nullptr)
result.add (String (getUrl (*scope).get()));
}
return result;
}
Array<URL> openDialogPreVista (bool async)
{
Array<URL> selections;
@@ -213,7 +349,7 @@ private:
{
OPENFILENAMEW of = {};
#ifdef OPENFILENAME_SIZE_VERSION_400W
#ifdef OPENFILENAME_SIZE_VERSION_400W
of.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
#else
of.lStructSize = sizeof (of);
@@ -231,16 +367,10 @@ private:
if (isSave)
{
StringArray tokens;
tokens.addTokens (filtersString, ";,", "\"'");
tokens.trim();
tokens.removeEmptyStrings();
auto extension = getDefaultFileExtension (files.getData());
if (tokens.size() == 1 && tokens[0].removeCharacters ("*.").isNotEmpty())
{
defaultExtension = tokens[0].fromFirstOccurrenceOf (".", false, false);
of.lpstrDefExt = defaultExtension.toWideCharPointer();
}
if (extension.isNotEmpty())
of.lpstrDefExt = extension.toWideCharPointer();
if (! GetSaveFileName (&of))
return {};
@@ -251,7 +381,7 @@ private:
return {};
}
if (selectMultiple && of.nFileOffset > 0 && files [of.nFileOffset - 1] == 0)
if (selectMultiple && of.nFileOffset > 0 && files[of.nFileOffset - 1] == 0)
{
const WCHAR* filename = files + of.nFileOffset;
@@ -267,18 +397,34 @@ private:
}
}
getNativeDialogList().removeValue (this);
return selections;
}
Array<URL> openDialog (bool async)
{
struct Remover
{
explicit Remover (Win32NativeFileChooser& chooser) : item (chooser) {}
~Remover() { getNativeDialogList().removeValue (&item); }
Win32NativeFileChooser& item;
};
const Remover remover (*this);
if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista)
return openDialogVistaAndUp (async);
return openDialogPreVista (async);
}
void run() override
{
// as long as the thread is running, don't delete this class
Ptr safeThis (this);
threadHasReference.signal();
Array<URL> r = openDialog (true);
auto r = openDialog (true);
MessageManager::callAsync ([safeThis, r]
{
safeThis->results = r;
@@ -330,6 +476,23 @@ private:
return ofFlags;
}
String getDefaultFileExtension (const String& filename) const
{
auto extension = filename.fromLastOccurrenceOf (".", false, false);
if (extension.isEmpty())
{
auto tokens = StringArray::fromTokens (filtersString, ";,", "\"'");
tokens.trim();
tokens.removeEmptyStrings();
if (tokens.size() == 1 && tokens[0].removeCharacters ("*.").isNotEmpty())
extension = tokens[0].fromFirstOccurrenceOf (".", false, false);
}
return extension;
}
//==============================================================================
void initialised (HWND hWnd)
{
@@ -414,7 +577,7 @@ private:
if (customComponent != nullptr && shouldCancel.get() == 0)
{
if (FilePreviewComponent* comp = dynamic_cast<FilePreviewComponent*> (customComponent->getChildComponent(0)))
if (FilePreviewComponent* comp = dynamic_cast<FilePreviewComponent*> (customComponent->getChildComponent (0)))
{
WCHAR path [MAX_PATH * 2] = { 0 };
CommDlg_OpenSave_GetFilePath (hdlg, (LPARAM) &path, MAX_PATH);
@@ -514,7 +677,6 @@ class FileChooser::Native : public Component,
public FileChooser::Pimpl
{
public:
Native (FileChooser& fileChooser, int flags, FilePreviewComponent* previewComp)
: owner (fileChooser),
nativeFileChooser (new Win32NativeFileChooser (this, flags, previewComp, fileChooser.startingFile,
@@ -531,7 +693,7 @@ public:
addToDesktop (0);
}
~Native()
~Native() override
{
exitModalState (0);
nativeFileChooser->cancel();


+ 1
- 4
source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp View File

@@ -3176,10 +3176,7 @@ private:
updateKeyModifiers();
if (hwnd == GetActiveWindow())
{
handleKeyPress (key, 0);
return true;
}
return handleKeyPress (key, 0);
}
return false;


+ 1
- 1
source/modules/juce_gui_basics/native/x11/juce_linux_X11_DragAndDrop.cpp View File

@@ -198,7 +198,7 @@ public:
srcMimeTypeAtomList.clear();
dragAndDropCurrentMimeType = 0;
auto dndCurrentVersion = static_cast<unsigned long> (clientMsg.data.l[1] & 0xff000000) >> 24;
auto dndCurrentVersion = (static_cast<unsigned long> (clientMsg.data.l[1]) & 0xff000000) >> 24;
if (dndCurrentVersion < 3 || dndCurrentVersion > XWindowSystemUtilities::Atoms::DndVersion)
{


+ 2
- 0
source/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.cpp View File

@@ -80,6 +80,7 @@ bool X11Symbols::loadAllSymbols()
using namespace X11SymbolHelpers;
if (! loadSymbols (xLib, xextLib,
makeSymbolBinding (xAllocClassHint, "XAllocClassHint"),
makeSymbolBinding (xAllocSizeHints, "XAllocSizeHints"),
makeSymbolBinding (xAllocWMHints, "XAllocWMHints"),
makeSymbolBinding (xBitmapBitOrder, "XBitmapBitOrder"),
@@ -169,6 +170,7 @@ bool X11Symbols::loadAllSymbols()
makeSymbolBinding (xScreenNumberOfScreen, "XScreenNumberOfScreen"),
makeSymbolBinding (xSelectInput, "XSelectInput"),
makeSymbolBinding (xSendEvent, "XSendEvent"),
makeSymbolBinding (xSetClassHint, "XSetClassHint"),
makeSymbolBinding (xSetErrorHandler, "XSetErrorHandler"),
makeSymbolBinding (xSetIOErrorHandler, "XSetIOErrorHandler"),
makeSymbolBinding (xSetInputFocus, "XSetInputFocus"),


+ 8
- 0
source/modules/juce_gui_basics/native/x11/juce_linux_X11_Symbols.h View File

@@ -49,6 +49,10 @@ public:
bool loadAllSymbols();
//==============================================================================
JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XAllocClassHint, xAllocClassHint,
(),
XClassHint*)
JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XAllocSizeHints, xAllocSizeHints,
(),
XSizeHints*)
@@ -405,6 +409,10 @@ public:
(::Display*, ::Window, Bool, long, XEvent*),
Status)
JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XSetClassHint, xSetClassHint,
(::Display*, ::Window, XClassHint*),
void)
JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XSetErrorHandler, xSetErrorHandler,
(XErrorHandler),
XErrorHandler)


+ 193
- 95
source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.cpp View File

@@ -643,16 +643,13 @@ namespace Visuals
}
//================================= X11 - Bitmap ===============================
static std::unordered_map<::Window, int> shmPaintsPendingMap;
class XBitmapImage : public ImagePixelData
{
public:
XBitmapImage (::Display* d, Image::PixelFormat format, int w, int h,
XBitmapImage (Image::PixelFormat format, int w, int h,
bool clearImage, unsigned int imageDepth_, Visual* visual)
: ImagePixelData (format, w, h),
imageDepth (imageDepth_),
display (d)
imageDepth (imageDepth_)
{
jassert (format == Image::RGB || format == Image::ARGB);
@@ -807,6 +804,11 @@ public:
{
XWindowSystemUtilities::ScopedXLock xLock;
#if JUCE_USE_XSHM
if (isUsingXShm())
XWindowSystem::getInstance()->addPendingPaintForWindow (window);
#endif
if (gc == None)
{
XGCValues gcvalues;
@@ -856,10 +858,7 @@ public:
// blit results to screen.
#if JUCE_USE_XSHM
if (isUsingXShm())
{
X11Symbols::getInstance()->xShmPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh, True);
++shmPaintsPendingMap[window];
}
else
#endif
X11Symbols::getInstance()->xPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh);
@@ -878,7 +877,7 @@ private:
int pixelStride, lineStride;
uint8* imageData = nullptr;
GC gc = None;
::Display* display = nullptr;
::Display* display = XWindowSystem::getInstance()->getDisplay();
#if JUCE_USE_XSHM
XShmSegmentInfo segmentInfo;
@@ -1155,7 +1154,7 @@ namespace ClipboardHelpers
// translate to utf8
numDataItems = localContent.getNumBytesAsUTF8() + 1;
data.calloc (numDataItems + 1);
data.calloc (numDataItems);
localContent.copyToUTF8 (data, numDataItems);
propertyFormat = 8; // bits/item
}
@@ -1165,7 +1164,7 @@ namespace ClipboardHelpers
numDataItems = 2;
propertyFormat = 32; // atoms are 32-bit
data.calloc (numDataItems * 4);
Atom* atoms = reinterpret_cast<Atom*> (data.getData());
Atom* atoms = unalignedPointerCast<Atom*> (data.getData());
atoms[0] = XWindowSystem::getInstance()->getAtoms().utf8String;
atoms[1] = XA_STRING;
@@ -1224,7 +1223,7 @@ ComponentPeer* getPeerFor (::Window windowH)
X11Symbols::getInstance()->xFindContext (display, (XID) windowH, windowHandleXContext, &peer);
}
return reinterpret_cast<ComponentPeer*> (peer);
return unalignedPointerCast<ComponentPeer*> (peer);
}
//==============================================================================
@@ -1305,6 +1304,13 @@ static int getAllEventsMask (bool ignoresMouseClicks)
XWindowSystemUtilities::ScopedXLock xLock;
auto root = X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display));
auto visualAndDepth = displayVisuals->getBestVisualForWindow ((styleFlags & ComponentPeer::windowIsSemiTransparent) != 0);
auto colormap = X11Symbols::getInstance()->xCreateColormap (display, root, visualAndDepth.visual, AllocNone);
X11Symbols::getInstance()->xInstallColormap (display, colormap);
// Set up the window attributes
XSetWindowAttributes swa;
swa.border_pixel = 0;
@@ -1313,11 +1319,9 @@ static int getAllEventsMask (bool ignoresMouseClicks)
swa.override_redirect = ((styleFlags & ComponentPeer::windowIsTemporary) != 0) ? True : False;
swa.event_mask = getAllEventsMask (styleFlags & ComponentPeer::windowIgnoresMouseClicks);
auto root = X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display));
auto windowH = X11Symbols::getInstance()->xCreateWindow (display, parentToAddTo != 0 ? parentToAddTo : root,
0, 0, 1, 1,
0, depth, InputOutput, visual,
0, visualAndDepth.depth, InputOutput, visualAndDepth.visual,
CWBorderPixel | CWColormap | CWBackPixmap | CWEventMask | CWOverrideRedirect,
&swa);
@@ -1334,12 +1338,28 @@ static int getAllEventsMask (bool ignoresMouseClicks)
}
// Set window manager hints
auto* wmHints = X11Symbols::getInstance()->xAllocWMHints();
wmHints->flags = InputHint | StateHint;
wmHints->input = True;
wmHints->initial_state = NormalState;
X11Symbols::getInstance()->xSetWMHints (display, windowH, wmHints);
X11Symbols::getInstance()->xFree (wmHints);
if (auto* wmHints = X11Symbols::getInstance()->xAllocWMHints())
{
wmHints->flags = InputHint | StateHint;
wmHints->input = True;
wmHints->initial_state = NormalState;
X11Symbols::getInstance()->xSetWMHints (display, windowH, wmHints);
X11Symbols::getInstance()->xFree (wmHints);
}
// Set class hint
if (auto* app = JUCEApplicationBase::getInstance())
{
if (auto* classHint = X11Symbols::getInstance()->xAllocClassHint())
{
auto appName = app->getApplicationName();
classHint->res_name = (char*) appName.getCharPointer().getAddress();
classHint->res_class = (char*) appName.getCharPointer().getAddress();
X11Symbols::getInstance()->xSetClassHint (display, windowH, classHint);
X11Symbols::getInstance()->xFree (classHint);
}
}
// Set the window type
setWindowType (windowH, styleFlags);
@@ -1407,7 +1427,10 @@ void XWindowSystem::destroyWindow (::Window windowH)
&event) == True)
{}
shmPaintsPendingMap.erase (windowH);
#if JUCE_USE_XSHM
if (XSHMHelpers::isShmAvailable (display))
shmPaintsPendingMap.erase (windowH);
#endif
}
//==============================================================================
@@ -1456,12 +1479,15 @@ void XWindowSystem::setIcon (::Window windowH, const Image& newIcon) const
if (wmHints == nullptr)
wmHints = X11Symbols::getInstance()->xAllocWMHints();
wmHints->flags |= IconPixmapHint | IconMaskHint;
wmHints->icon_pixmap = PixmapHelpers::createColourPixmapFromImage (display, newIcon);
wmHints->icon_mask = PixmapHelpers::createMaskPixmapFromImage (display, newIcon);
if (wmHints != nullptr)
{
wmHints->flags |= IconPixmapHint | IconMaskHint;
wmHints->icon_pixmap = PixmapHelpers::createColourPixmapFromImage (display, newIcon);
wmHints->icon_mask = PixmapHelpers::createMaskPixmapFromImage (display, newIcon);
X11Symbols::getInstance()->xSetWMHints (display, windowH, wmHints);
X11Symbols::getInstance()->xFree (wmHints);
X11Symbols::getInstance()->xSetWMHints (display, windowH, wmHints);
X11Symbols::getInstance()->xFree (wmHints);
}
X11Symbols::getInstance()->xSync (display, False);
}
@@ -1514,22 +1540,24 @@ void XWindowSystem::setBounds (::Window windowH, Rectangle<int> newBounds, bool
XWindowSystemUtilities::ScopedXLock xLock;
auto* hints = X11Symbols::getInstance()->xAllocSizeHints();
hints->flags = USSize | USPosition;
hints->x = newBounds.getX();
hints->y = newBounds.getY();
hints->width = newBounds.getWidth();
hints->height = newBounds.getHeight();
if ((peer->getStyleFlags() & ComponentPeer::windowIsResizable) == 0)
if (auto* hints = X11Symbols::getInstance()->xAllocSizeHints())
{
hints->min_width = hints->max_width = hints->width;
hints->min_height = hints->max_height = hints->height;
hints->flags |= PMinSize | PMaxSize;
}
hints->flags = USSize | USPosition;
hints->x = newBounds.getX();
hints->y = newBounds.getY();
hints->width = newBounds.getWidth();
hints->height = newBounds.getHeight();
X11Symbols::getInstance()->xSetWMNormalHints (display, windowH, hints);
X11Symbols::getInstance()->xFree (hints);
if ((peer->getStyleFlags() & ComponentPeer::windowIsResizable) == 0)
{
hints->min_width = hints->max_width = hints->width;
hints->min_height = hints->max_height = hints->height;
hints->flags |= PMinSize | PMaxSize;
}
X11Symbols::getInstance()->xSetWMNormalHints (display, windowH, hints);
X11Symbols::getInstance()->xFree (hints);
}
auto windowBorder = peer->getFrameSize();
@@ -1787,16 +1815,18 @@ bool XWindowSystem::canUseARGBImages() const
return canUseARGB;
}
Image XWindowSystem::createImage (int width, int height, bool argb) const
Image XWindowSystem::createImage (bool isSemiTransparent, int width, int height, bool argb) const
{
auto visualAndDepth = displayVisuals->getBestVisualForWindow (isSemiTransparent);
#if JUCE_USE_XSHM
return Image (new XBitmapImage (display, argb ? Image::ARGB : Image::RGB,
return Image (new XBitmapImage (argb ? Image::ARGB : Image::RGB,
#else
return Image (new XBitmapImage (display, Image::RGB,
return Image (new XBitmapImage (Image::RGB,
#endif
(width + 31) & ~31,
(height + 31) & ~31,
false, (unsigned int) depth, visual));
false, (unsigned int) visualAndDepth.depth, visualAndDepth.visual));
}
void XWindowSystem::blitToWindow (::Window windowH, Image image, Rectangle<int> destinationRect, Rectangle<int> totalRect) const
@@ -1812,20 +1842,47 @@ void XWindowSystem::blitToWindow (::Window windowH, Image image, Rectangle<int>
destinationRect.getX() - totalRect.getX(), destinationRect.getY() - totalRect.getY());
}
int XWindowSystem::getNumPaintsPending (::Window windowH) const
void XWindowSystem::processPendingPaintsForWindow (::Window windowH)
{
#if JUCE_USE_XSHM
if (shmPaintsPendingMap[windowH] != 0)
if (! XSHMHelpers::isShmAvailable (display))
return;
if (getNumPaintsPendingForWindow (windowH) > 0)
{
XWindowSystemUtilities::ScopedXLock xLock;
XEvent evt;
while (X11Symbols::getInstance()->xCheckTypedWindowEvent (display, windowH, shmCompletionEvent, &evt))
--shmPaintsPendingMap[windowH];
removePendingPaintForWindow (windowH);
}
#endif
}
return shmPaintsPendingMap[windowH];
int XWindowSystem::getNumPaintsPendingForWindow (::Window windowH)
{
#if JUCE_USE_XSHM
if (XSHMHelpers::isShmAvailable (display))
return shmPaintsPendingMap[windowH];
#endif
return 0;
}
void XWindowSystem::addPendingPaintForWindow (::Window windowH)
{
#if JUCE_USE_XSHM
if (XSHMHelpers::isShmAvailable (display))
++shmPaintsPendingMap[windowH];
#endif
}
void XWindowSystem::removePendingPaintForWindow (::Window windowH)
{
#if JUCE_USE_XSHM
if (XSHMHelpers::isShmAvailable (display))
--shmPaintsPendingMap[windowH];
#endif
}
void XWindowSystem::setScreenSaverEnabled (bool enabled) const
@@ -2107,24 +2164,39 @@ ModifierKeys XWindowSystem::getNativeRealtimeModifiers() const
return ModifierKeys::currentModifiers;
}
Array<Displays::Display> XWindowSystem::findDisplays (float masterScale) const
static bool hasWorkAreaData (const XWindowSystemUtilities::GetXProperty& prop)
{
Array<Displays::Display> displays;
Atom hints = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WORKAREA");
return prop.success
&& prop.actualType == XA_CARDINAL
&& prop.actualFormat == 32
&& prop.numItems == 4
&& prop.data != nullptr;
}
auto getWorkAreaPropertyData = [&] (int screenNum) -> unsigned char*
static Rectangle<int> getWorkArea (const XWindowSystemUtilities::GetXProperty& prop)
{
if (hasWorkAreaData (prop))
{
if (hints != None)
{
XWindowSystemUtilities::GetXProperty prop (X11Symbols::getInstance()->xRootWindow (display, screenNum), hints, 0, 4, false, XA_CARDINAL);
auto* positionData = prop.data;
std::array<long, 4> position;
if (prop.success && prop.actualType == XA_CARDINAL && prop.actualFormat == 32 && prop.numItems == 4)
return prop.data;
for (auto& p : position)
{
memcpy (&p, positionData, sizeof (long));
positionData += sizeof (long);
}
return nullptr;
};
return { (int) position[0], (int) position[1],
(int) position[2], (int) position[3] };
}
return {};
}
Array<Displays::Display> XWindowSystem::findDisplays (float masterScale) const
{
Array<Displays::Display> displays;
auto workAreaHints = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WORKAREA");
#if JUCE_USE_XRANDR
{
@@ -2137,11 +2209,13 @@ Array<Displays::Display> XWindowSystem::findDisplays (float masterScale) const
for (int i = 0; i < numMonitors; ++i)
{
if (getWorkAreaPropertyData (i) == nullptr)
auto rootWindow = X11Symbols::getInstance()->xRootWindow (display, i);
XWindowSystemUtilities::GetXProperty prop (rootWindow, workAreaHints, 0, 4, false, XA_CARDINAL);
if (! hasWorkAreaData (prop))
continue;
if (auto* screens = X11Symbols::getInstance()->xRRGetScreenResources (display,
X11Symbols::getInstance()->xRootWindow (display, i)))
if (auto* screens = X11Symbols::getInstance()->xRRGetScreenResources (display, rootWindow))
{
for (int j = 0; j < screens->noutput; ++j)
{
@@ -2225,25 +2299,22 @@ Array<Displays::Display> XWindowSystem::findDisplays (float masterScale) const
if (displays.isEmpty())
#endif
{
if (hints != None)
if (workAreaHints != None)
{
auto numMonitors = X11Symbols::getInstance()->xScreenCount (display);
for (int i = 0; i < numMonitors; ++i)
{
if (auto* positionData = getWorkAreaPropertyData (i))
{
std::array<long, 4> position;
XWindowSystemUtilities::GetXProperty prop (X11Symbols::getInstance()->xRootWindow (display, i),
workAreaHints, 0, 4, false, XA_CARDINAL);
for (auto& p : position)
{
memcpy (&p, positionData, sizeof (long));
positionData += sizeof (long);
}
auto workArea = getWorkArea (prop);
if (! workArea.isEmpty())
{
Displays::Display d;
d.totalArea = { (int) position[0], (int) position[1],
(int) position[2], (int) position[3] };
d.totalArea = workArea;
d.isMain = displays.isEmpty();
d.scale = masterScale;
d.dpi = DisplayHelpers::getDisplayDPI (display, i);
@@ -2675,6 +2746,40 @@ long XWindowSystem::getUserTime (::Window windowH) const
return result;
}
XWindowSystem::DisplayVisuals::DisplayVisuals (::Display* xDisplay)
{
auto findVisualWithDepthOrNull = [&] (int desiredDepth) -> Visual*
{
int matchedDepth = 0;
auto* visual = Visuals::findVisualFormat (xDisplay, desiredDepth, matchedDepth);
if (desiredDepth == matchedDepth)
return visual;
return nullptr;
};
visual16Bit = findVisualWithDepthOrNull (16);
visual24Bit = findVisualWithDepthOrNull (24);
visual32Bit = findVisualWithDepthOrNull (32);
}
XWindowSystem::VisualAndDepth XWindowSystem::DisplayVisuals::getBestVisualForWindow (bool isSemiTransparent) const
{
if (isSemiTransparent && visual32Bit != nullptr)
return { visual32Bit, 32 };
if (visual24Bit != nullptr)
return { visual24Bit, 24 };
return { visual16Bit, 16 };
}
bool XWindowSystem::DisplayVisuals::isValid() const noexcept
{
return (visual32Bit != nullptr || visual24Bit != nullptr || visual16Bit != nullptr);
}
//==============================================================================
bool XWindowSystem::initialiseXDisplay()
{
@@ -2708,7 +2813,8 @@ bool XWindowSystem::initialiseXDisplay()
// Create our message window (this will never be mapped)
auto screen = X11Symbols::getInstance()->xDefaultScreen (display);
juce_messageWindowHandle = X11Symbols::getInstance()->xCreateWindow (display, X11Symbols::getInstance()->xRootWindow (display, screen),
auto root = X11Symbols::getInstance()->xRootWindow (display, screen);
juce_messageWindowHandle = X11Symbols::getInstance()->xCreateWindow (display, root,
0, 0, 1, 1, 0, 0, InputOnly,
X11Symbols::getInstance()->xDefaultVisual (display, screen),
CWEventMask, &swa);
@@ -2717,22 +2823,6 @@ bool XWindowSystem::initialiseXDisplay()
atoms = XWindowSystemUtilities::Atoms (display);
// Get defaults for various properties
auto root = X11Symbols::getInstance()->xRootWindow (display, screen);
// Try to obtain a 32-bit visual or fallback to 24 or 16
visual = Visuals::findVisualFormat (display, 32, depth);
if (visual == nullptr)
{
Logger::outputDebugString ("ERROR: System doesn't support 32, 24 or 16 bit RGB display.\n");
Process::terminate();
}
// Create and install a colormap suitable for our visual
colormap = X11Symbols::getInstance()->xCreateColormap (display, root, visual, AllocNone);
X11Symbols::getInstance()->xInstallColormap (display, colormap);
initialisePointerMap();
updateModifierMappings();
@@ -2741,6 +2831,14 @@ bool XWindowSystem::initialiseXDisplay()
shmCompletionEvent = X11Symbols::getInstance()->xShmGetEventBase (display) + ShmCompletion;
#endif
displayVisuals = std::make_unique<DisplayVisuals> (display);
if (! displayVisuals->isValid())
{
Logger::outputDebugString ("ERROR: System doesn't support 32, 24 or 16 bit RGB display.\n");
Process::terminate();
}
// Setup input event handler
LinuxEventLoop::registerFdCallback (X11Symbols::getInstance()->xConnectionNumber (display),
[this] (int)
@@ -2788,10 +2886,10 @@ void XWindowSystem::destroyXDisplay()
X11Symbols::getInstance()->xSync (display, True);
LinuxEventLoop::unregisterFdCallback (X11Symbols::getInstance()->xConnectionNumber (display));
visual = nullptr;
X11Symbols::getInstance()->xCloseDisplay (display);
display = nullptr;
displayVisuals = nullptr;
}
}
@@ -2870,7 +2968,7 @@ void XWindowSystem::handleWindowMessage (LinuxComponentPeer<::Window>* peer, XEv
XWindowSystemUtilities::ScopedXLock xLock;
if (event.xany.type == shmCompletionEvent)
--shmPaintsPendingMap[(::Window) peer->getNativeHandle()];
XWindowSystem::getInstance()->removePendingPaintForWindow ((::Window) peer->getNativeHandle());
}
#endif
break;
@@ -2978,7 +3076,7 @@ void XWindowSystem::handleKeyPressEvent (LinuxComponentPeer<::Window>* peer, XKe
if (sym >= XK_F1 && sym <= XK_F35)
{
keyPressed = true;
keyCode = (sym & 0xff) | Keys::extendedKeyModifier;
keyCode = static_cast<int> ((sym & 0xff) | Keys::extendedKeyModifier);
}
break;
}


+ 29
- 5
source/modules/juce_gui_basics/native/x11/juce_linux_XWindowSystem.h View File

@@ -126,9 +126,12 @@ public:
bool canUseSemiTransparentWindows() const;
bool canUseARGBImages() const;
int getNumPaintsPending (::Window windowH) const;
int getNumPaintsPendingForWindow (::Window windowH);
void processPendingPaintsForWindow (::Window windowH);
void addPendingPaintForWindow (::Window windowH);
void removePendingPaintForWindow (::Window windowH);
Image createImage (int width, int height, bool argb) const;
Image createImage (bool isSemiTransparentWindow, int width, int height, bool argb) const;
void blitToWindow (::Window windowH, Image image, Rectangle<int> destinationRect, Rectangle<int> totalRect) const;
void setScreenSaverEnabled (bool enabled) const;
@@ -171,6 +174,24 @@ private:
~XWindowSystem();
//==============================================================================
struct VisualAndDepth
{
Visual* visual;
int depth;
};
struct DisplayVisuals
{
explicit DisplayVisuals (::Display* d);
VisualAndDepth getBestVisualForWindow (bool isSemiTransparent) const;
bool isValid() const noexcept;
Visual* visual16Bit = nullptr;
Visual* visual24Bit = nullptr;
Visual* visual32Bit = nullptr;
};
bool initialiseXDisplay();
void destroyXDisplay();
@@ -217,10 +238,13 @@ private:
XWindowSystemUtilities::Atoms atoms;
::Display* display = nullptr;
Colormap colormap = {};
Visual* visual = nullptr;
std::unique_ptr<DisplayVisuals> displayVisuals;
#if JUCE_USE_XSHM
std::map<::Window, int> shmPaintsPendingMap;
#endif
int depth = 0, shmCompletionEvent = 0;
int shmCompletionEvent = 0;
int pointerMap[5] = {};
String localClipboardContent;


+ 1
- 1
source/modules/juce_gui_basics/properties/juce_MultiChoicePropertyComponent.cpp View File

@@ -42,7 +42,7 @@ public:
}
};
void updateButtonTickColour (ToggleButton* button, bool usingDefault)
static void updateButtonTickColour (ToggleButton* button, bool usingDefault)
{
button->setColour (ToggleButton::tickColourId, button->getLookAndFeel().findColour (ToggleButton::tickColourId)
.withAlpha (usingDefault ? 0.4f : 1.0f));


+ 172
- 89
source/modules/juce_gui_basics/widgets/juce_TextEditor.cpp View File

@@ -278,10 +278,11 @@ struct TextEditor::Iterator
Iterator (const TextEditor& ed)
: sections (ed.sections),
justification (ed.justification),
justificationWidth (ed.getJustificationWidth()),
bottomRight (ed.getMaximumWidth(), ed.getMaximumHeight()),
wordWrapWidth (ed.getWordWrapWidth()),
passwordCharacter (ed.passwordCharacter),
lineSpacing (ed.lineSpacing)
lineSpacing (ed.lineSpacing),
underlineWhitespace (ed.underlineWhitespace)
{
jassert (wordWrapWidth > 0);
@@ -292,6 +293,8 @@ struct TextEditor::Iterator
if (currentSection != nullptr)
beginNewLine();
}
lineHeight = ed.currentFont.getHeight();
}
Iterator (const Iterator&) = default;
@@ -325,7 +328,7 @@ struct TextEditor::Iterator
{
tempAtom.numChars = (uint16) split;
tempAtom.width = g.getGlyph (split - 1).getRight();
atomX = getJustificationOffset (tempAtom.width);
atomX = getJustificationOffsetX (tempAtom.width);
atomRight = atomX + tempAtom.width;
return true;
}
@@ -427,14 +430,14 @@ struct TextEditor::Iterator
tempAtom.numChars = 0;
atom = &tempAtom;
if (atomX > justificationOffset)
if (atomX > justificationOffsetX)
beginNewLine();
return next();
}
beginNewLine();
atomX = justificationOffset;
atomX = justificationOffsetX;
atomRight = atomX + atom->width;
return true;
}
@@ -494,25 +497,22 @@ struct TextEditor::Iterator
++tempAtomIndex;
}
justificationOffset = getJustificationOffset (lineWidth);
atomX = justificationOffset;
justificationOffsetX = getJustificationOffsetX (lineWidth);
atomX = justificationOffsetX;
}
float getJustificationOffset (float lineWidth) const
float getJustificationOffsetX (float lineWidth) const
{
if (justification.getOnlyHorizontalFlags() == Justification::horizontallyCentred)
return jmax (0.0f, (justificationWidth - lineWidth) * 0.5f);
if (justification.getOnlyHorizontalFlags() == Justification::right)
return jmax (0.0f, justificationWidth - lineWidth);
if (justification.testFlags (Justification::horizontallyCentred)) return jmax (0.0f, (bottomRight.x - lineWidth) * 0.5f);
if (justification.testFlags (Justification::right)) return jmax (0.0f, bottomRight.x - lineWidth);
return 0;
}
//==============================================================================
void draw (Graphics& g, const UniformTextSection*& lastSection) const
void draw (Graphics& g, const UniformTextSection*& lastSection, AffineTransform transform) const
{
if (passwordCharacter != 0 || ! atom->isWhitespace())
if (passwordCharacter != 0 || (underlineWhitespace || ! atom->isWhitespace()))
{
if (lastSection != currentSection)
{
@@ -527,7 +527,7 @@ struct TextEditor::Iterator
ga.addLineOfText (currentSection->font,
atom->getTrimmedText (passwordCharacter),
atomX, (float) roundToInt (lineY + lineHeight - maxDescent));
ga.draw (g);
ga.draw (g, transform);
}
}
@@ -539,18 +539,19 @@ struct TextEditor::Iterator
area.add (startX, lineY, endX - startX, lineHeight * lineSpacing);
}
void drawUnderline (Graphics& g, Range<int> underline, Colour colour) const
void drawUnderline (Graphics& g, Range<int> underline, Colour colour, AffineTransform transform) const
{
auto startX = roundToInt (indexToX (underline.getStart()));
auto endX = roundToInt (indexToX (underline.getEnd()));
auto baselineY = roundToInt (lineY + currentSection->font.getAscent() + 0.5f);
Graphics::ScopedSaveState state (g);
g.addTransform (transform);
g.reduceClipRegion ({ startX, baselineY, endX - startX, 1 });
g.fillCheckerBoard ({ (float) endX, (float) baselineY + 1.0f }, 3.0f, 1.0f, colour, Colours::transparentBlack);
}
void drawSelectedText (Graphics& g, Range<int> selected, Colour selectedTextColour) const
void drawSelectedText (Graphics& g, Range<int> selected, Colour selectedTextColour, AffineTransform transform) const
{
if (passwordCharacter != 0 || ! atom->isWhitespace())
{
@@ -566,7 +567,7 @@ struct TextEditor::Iterator
ga.removeRangeOfGlyphs (selected.getEnd() - indexInText, -1);
g.setColour (currentSection->colour);
ga2.draw (g);
ga2.draw (g, transform);
}
if (selected.getStart() > indexInText)
@@ -576,11 +577,11 @@ struct TextEditor::Iterator
ga.removeRangeOfGlyphs (0, selected.getStart() - indexInText);
g.setColour (currentSection->colour);
ga2.draw (g);
ga2.draw (g, transform);
}
g.setColour (selectedTextColour);
ga.draw (g);
ga.draw (g, transform);
}
}
@@ -649,20 +650,42 @@ struct TextEditor::Iterator
return false;
}
float getYOffset()
{
if (justification.testFlags (Justification::top) || lineY >= bottomRight.y)
return 0;
while (next())
{
if (lineY >= bottomRight.y)
return 0;
}
auto bottom = jmax (0.0f, bottomRight.y - lineY - lineHeight);
if (justification.testFlags (Justification::bottom))
return bottom;
return bottom * 0.5f;
}
//==============================================================================
int indexInText = 0;
float lineY = 0, justificationOffset = 0, lineHeight = 0, maxDescent = 0;
float lineY = 0, lineHeight = 0, maxDescent = 0;
float atomX = 0, atomRight = 0;
const TextAtom* atom = nullptr;
const UniformTextSection* currentSection = nullptr;
private:
const OwnedArray<UniformTextSection>& sections;
const UniformTextSection* currentSection = nullptr;
int sectionIndex = 0, atomIndex = 0;
Justification justification;
const float justificationWidth, wordWrapWidth;
float justificationOffsetX = 0;
const Point<float> bottomRight;
const float wordWrapWidth;
const juce_wchar passwordCharacter;
const float lineSpacing;
const bool underlineWhitespace;
TextAtom tempAtom;
void moveToEndOfLastAtom()
@@ -673,7 +696,7 @@ private:
if (atom->isNewLine())
{
atomX = 0.0f;
atomX = getJustificationOffsetX (0);
lineY += lineHeight * lineSpacing;
}
}
@@ -826,8 +849,8 @@ struct TextEditor::TextEditorViewport : public Viewport
void visibleAreaChanged (const Rectangle<int>&) override
{
if (! rentrant) // it's rare, but possible to get into a feedback loop as the viewport's scrollbars
// appear and disappear, causing the wrap width to change.
if (! reentrant) // it's rare, but possible to get into a feedback loop as the viewport's scrollbars
// appear and disappear, causing the wrap width to change.
{
auto wordWrapWidth = owner.getWordWrapWidth();
@@ -835,9 +858,8 @@ struct TextEditor::TextEditorViewport : public Viewport
{
lastWordWrapWidth = wordWrapWidth;
rentrant = true;
owner.updateTextHolderSize();
rentrant = false;
ScopedValueSetter<bool> svs (reentrant, true);
owner.checkLayout();
}
}
}
@@ -845,7 +867,7 @@ struct TextEditor::TextEditorViewport : public Viewport
private:
TextEditor& owner;
float lastWordWrapWidth = 0;
bool rentrant = false;
bool reentrant = false;
JUCE_DECLARE_NON_COPYABLE (TextEditorViewport)
};
@@ -936,8 +958,8 @@ void TextEditor::setMultiLine (const bool shouldBeMultiLine,
multiline = shouldBeMultiLine;
wordWrap = shouldWordWrap && shouldBeMultiLine;
viewport->setScrollBarsShown (scrollbarVisible && multiline,
scrollbarVisible && multiline);
checkLayout();
viewport->setViewPosition (0, 0);
resized();
scrollToMakeSureCursorIsVisible();
@@ -954,8 +976,7 @@ void TextEditor::setScrollbarsShown (bool shown)
if (scrollbarVisible != shown)
{
scrollbarVisible = shown;
shown = shown && isMultiLine();
viewport->setScrollBarsShown (shown, shown);
checkLayout();
}
}
@@ -1003,7 +1024,9 @@ void TextEditor::setJustification (Justification j)
if (justification != j)
{
justification = j;
resized();
repaint();
}
}
@@ -1028,7 +1051,7 @@ void TextEditor::applyFontToAllText (const Font& newFont, bool changeCurrentFont
}
coalesceSimilarSections();
updateTextHolderSize();
checkLayout();
scrollToMakeSureCursorIsVisible();
repaint();
}
@@ -1090,8 +1113,13 @@ void TextEditor::recreateCaret()
void TextEditor::updateCaretPosition()
{
if (caret != nullptr)
caret->setCaretPosition (getCaretRectangle().translated (leftIndent, topIndent));
if (caret != nullptr
&& getWidth() > 0 && getHeight() > 0)
{
Iterator i (*this);
caret->setCaretPosition (getCaretRectangle().translated (leftIndent,
topIndent + roundToInt (i.getYOffset())));
}
}
TextEditor::LengthAndCharacterRestriction::LengthAndCharacterRestriction (int maxLen, const String& chars)
@@ -1146,7 +1174,7 @@ void TextEditor::setScrollBarThickness (int newThicknessPixels)
void TextEditor::clear()
{
clearInternal (nullptr);
updateTextHolderSize();
checkLayout();
undoManager.clearUndoHistory();
}
@@ -1181,7 +1209,7 @@ void TextEditor::setText (const String& newText, bool sendTextChangeMessage)
else
textValue.addListener (textHolder);
updateTextHolderSize();
checkLayout();
scrollToMakeSureCursorIsVisible();
undoManager.clearUndoHistory();
@@ -1214,7 +1242,7 @@ void TextEditor::textWasChangedByValue()
//==============================================================================
void TextEditor::textChanged()
{
updateTextHolderSize();
checkLayout();
if (listeners.size() != 0 || onTextChange != nullptr)
postCommandMessage (TextEditorDefs::textChangeMessageId);
@@ -1259,30 +1287,33 @@ void TextEditor::repaintText (Range<int> range)
{
if (! range.isEmpty())
{
auto lh = currentFont.getHeight();
auto wordWrapWidth = getWordWrapWidth();
if (wordWrapWidth > 0)
if (range.getEnd() >= getTotalNumChars())
{
Point<float> anchor;
Iterator i (*this);
i.getCharPosition (range.getStart(), anchor, lh);
textHolder->repaint();
return;
}
auto y1 = (int) anchor.y;
int y2;
Iterator i (*this);
if (range.getEnd() >= getTotalNumChars())
{
y2 = textHolder->getHeight();
}
else
{
i.getCharPosition (range.getEnd(), anchor, lh);
y2 = (int) (anchor.y + lh * 2.0f);
}
Point<float> anchor;
auto lh = currentFont.getHeight();
i.getCharPosition (range.getStart(), anchor, lh);
textHolder->repaint (0, y1, textHolder->getWidth(), y2 - y1);
auto y1 = std::trunc (anchor.y);
int y2 = 0;
if (range.getEnd() >= getTotalNumChars())
{
y2 = textHolder->getHeight();
}
else
{
i.getCharPosition (range.getEnd(), anchor, lh);
y2 = (int) (anchor.y + lh * 2.0f);
}
auto offset = i.getYOffset();
textHolder->repaint (0, roundToInt (y1 + offset), textHolder->getWidth(), roundToInt ((float) y2 - y1 + offset));
}
}
@@ -1367,33 +1398,66 @@ Rectangle<float> TextEditor::getCaretRectangleFloat() const
}
//==============================================================================
enum { rightEdgeSpace = 2 };
// Extra space for the cursor at the right-hand-edge
constexpr int rightEdgeSpace = 2;
float TextEditor::getWordWrapWidth() const
{
return wordWrap ? getJustificationWidth()
return wordWrap ? getMaximumWidth()
: std::numeric_limits<float>::max();
}
float TextEditor::getJustificationWidth() const
float TextEditor::getMaximumWidth() const
{
return (float) (viewport->getMaximumVisibleWidth() - (leftIndent + rightEdgeSpace + 1));
}
void TextEditor::updateTextHolderSize()
float TextEditor::getMaximumHeight() const
{
return (float) (viewport->getMaximumVisibleHeight() - topIndent);
}
void TextEditor::checkLayout()
{
if (getWordWrapWidth() > 0)
{
float maxWidth = getJustificationWidth();
auto maxWidth = getMaximumWidth();
Iterator i (*this);
while (i.next())
maxWidth = jmax (maxWidth, i.atomRight);
auto w = leftIndent + roundToInt (maxWidth);
auto h = topIndent + roundToInt (jmax (i.lineY + i.lineHeight, currentFont.getHeight()));
auto textRight = roundToInt (maxWidth);
auto textBottom = roundToInt (i.lineY + i.lineHeight + i.getYOffset());
textHolder->setSize (w + rightEdgeSpace, h + 1); // (allows a bit of space for the cursor to be at the right-hand-edge)
if (i.atom != nullptr && i.atom->isNewLine())
textBottom += (int) i.lineHeight;
updateTextHolderSize (textRight, textBottom);
updateScrollbarVisibility (textRight, textBottom);
}
}
void TextEditor::updateTextHolderSize (int textRight, int textBottom)
{
auto w = leftIndent + jmax (roundToInt (getMaximumWidth()), textRight);
auto h = topIndent + textBottom;
textHolder->setSize (w + rightEdgeSpace, h + 1);
}
void TextEditor::updateScrollbarVisibility (int textRight, int textBottom)
{
if (scrollbarVisible && multiline)
{
auto horizontalVisible = (leftIndent + textRight) > (viewport->getMaximumVisibleWidth() - viewport->getScrollBarThickness());
auto verticalVisible = (topIndent + textBottom) > (viewport->getMaximumVisibleHeight() + 1);
viewport->setScrollBarsShown (verticalVisible, horizontalVisible);
}
else
{
viewport->setScrollBarsShown (false, false);
}
}
@@ -1402,8 +1466,14 @@ int TextEditor::getTextHeight() const { return textHolder->getHeight(); }
void TextEditor::setIndents (int newLeftIndent, int newTopIndent)
{
leftIndent = newLeftIndent;
topIndent = newTopIndent;
if (leftIndent != newLeftIndent || topIndent != newTopIndent)
{
leftIndent = newLeftIndent;
topIndent = newTopIndent;
resized();
repaint();
}
}
void TextEditor::setBorder (BorderSize<int> border)
@@ -1506,8 +1576,10 @@ void TextEditor::moveCaretTo (const int newPosition, const bool isSelecting)
int TextEditor::getTextIndexAt (const int x, const int y)
{
Iterator i (*this);
return indexAtPosition ((float) (x + viewport->getViewPositionX() - leftIndent - borderSize.getLeft()),
(float) (y + viewport->getViewPositionY() - topIndent - borderSize.getTop()));
(float) (y + viewport->getViewPositionY() - topIndent - borderSize.getTop()) - i.getYOffset());
}
void TextEditor::insertTextAtCaret (const String& t)
@@ -1576,8 +1648,19 @@ void TextEditor::drawContent (Graphics& g)
{
g.setOrigin (leftIndent, topIndent);
auto clip = g.getClipBounds();
Colour selectedTextColour;
auto yOffset = Iterator (*this).getYOffset();
AffineTransform transform;
if (yOffset > 0)
{
transform = AffineTransform::translation (0.0f, yOffset);
clip.setY (roundToInt ((float) clip.getY() - yOffset));
}
Iterator i (*this);
Colour selectedTextColour;
if (! selection.isEmpty())
{
@@ -1593,10 +1676,10 @@ void TextEditor::drawContent (Graphics& g)
}
}
g.setColour (findColour (highlightColourId).withMultipliedAlpha (hasKeyboardFocus (true) ? 1.0f : 0.5f));
g.fillRectList (selectionArea);
selectedTextColour = findColour (highlightedTextColourId);
g.setColour (findColour (highlightColourId).withMultipliedAlpha (hasKeyboardFocus (true) ? 1.0f : 0.5f));
g.fillPath (selectionArea.toPath(), transform);
}
const UniformTextSection* lastSection = nullptr;
@@ -1607,12 +1690,12 @@ void TextEditor::drawContent (Graphics& g)
{
if (selection.intersects ({ i.indexInText, i.indexInText + i.atom->numChars }))
{
i.drawSelectedText (g, selection, selectedTextColour);
i.drawSelectedText (g, selection, selectedTextColour, transform);
lastSection = nullptr;
}
else
{
i.draw (g, lastSection);
i.draw (g, lastSection, transform);
}
}
}
@@ -1626,7 +1709,7 @@ void TextEditor::drawContent (Graphics& g)
if (i2.lineY + i2.lineHeight >= (float) clip.getY()
&& underlinedSection.intersects ({ i2.indexInText, i2.indexInText + i2.atom->numChars }))
{
i2.drawUnderline (g, underlinedSection, findColour (textColourId));
i2.drawUnderline (g, underlinedSection, findColour (textColourId), transform);
}
}
}
@@ -1647,13 +1730,10 @@ void TextEditor::paintOverChildren (Graphics& g)
g.setColour (colourForTextWhenEmpty);
g.setFont (getFont());
if (isMultiLine())
g.drawText (textToShowWhenEmpty, getLocalBounds(),
Justification::centred, true);
else
g.drawText (textToShowWhenEmpty,
leftIndent, 0, viewport->getWidth() - leftIndent, getHeight(),
Justification::centredLeft, true);
g.drawText (textToShowWhenEmpty,
leftIndent, topIndent,
viewport->getWidth() - leftIndent, getHeight() - topIndent,
justification, true);
}
getLookAndFeel().drawTextEditorOutline (g, getWidth(), getHeight(), *this);
@@ -2055,7 +2135,7 @@ bool TextEditor::keyStateChanged (const bool isKeyDown)
}
//==============================================================================
void TextEditor::focusGained (FocusChangeType)
void TextEditor::focusGained (FocusChangeType cause)
{
newTransaction();
@@ -2067,6 +2147,9 @@ void TextEditor::focusGained (FocusChangeType)
checkFocus();
if (cause == FocusChangeType::focusChangedByMouseClick && selectAllTextWhenFocused)
wasFocused = false;
repaint();
updateCaretPosition();
}
@@ -2095,7 +2178,7 @@ void TextEditor::resized()
viewport->setBoundsInset (borderSize);
viewport->setSingleStepSizes (16, roundToInt (currentFont.getHeight()));
updateTextHolderSize();
checkLayout();
if (isMultiLine())
updateCaretPosition();
@@ -2213,7 +2296,7 @@ void TextEditor::insert (const String& text, int insertIndex, const Font& font,
totalNumChars = -1;
valueTextNeedsUpdating = true;
updateTextHolderSize();
checkLayout();
moveCaretTo (caretPositionToMoveTo, false);
repaintText ({ insertIndex, getTotalNumChars() });
@@ -2426,7 +2509,7 @@ void TextEditor::getCharPosition (int index, Point<float>& anchor, float& lineHe
if (sections.isEmpty())
{
anchor = { i.getJustificationOffset (0), 0 };
anchor = { i.getJustificationOffsetX (0), 0 };
lineHeight = currentFont.getHeight();
}
else


+ 24
- 7
source/modules/juce_gui_basics/widgets/juce_TextEditor.h View File

@@ -155,7 +155,6 @@ public:
*/
bool areScrollbarsShown() const noexcept { return scrollbarVisible; }
/** Changes the password character used to disguise the text.
@param passwordCharacter if this is not zero, this character will be used as a replacement
@@ -172,7 +171,6 @@ public:
*/
juce_wchar getPasswordCharacter() const noexcept { return passwordCharacter; }
//==============================================================================
/** Allows a right-click menu to appear for the editor.
@@ -251,7 +249,7 @@ public:
@see setFont
*/
const Font& getFont() const noexcept { return currentFont; }
const Font& getFont() const noexcept { return currentFont; }
/** Applies a colour to all the text in the editor.
@@ -260,6 +258,18 @@ public:
*/
void applyColourToAllText (const Colour& newColour, bool changeCurrentTextColour = true);
/** Sets whether whitespace should be underlined when the editor font is underlined.
@see isWhitespaceUnderlined
*/
void setWhitespaceUnderlined (bool shouldUnderlineWhitespace) noexcept { underlineWhitespace = shouldUnderlineWhitespace; }
/** Returns true if whitespace is underlined for underlined fonts.
@see setWhitespaceIsUnderlined
*/
bool isWhitespaceUnderlined() const noexcept { return underlineWhitespace; }
//==============================================================================
/** If set to true, focusing on the editor will highlight all its text.
@@ -495,9 +505,12 @@ public:
*/
void setScrollToShowCursor (bool shouldScrollToShowCaret);
/** Modifies the horizontal justification of the text within the editor window. */
/** Modifies the justification of the text within the editor window. */
void setJustification (Justification newJustification);
/** Returns the type of justification, as set in setJustification(). */
Justification getJustificationType() const noexcept { return justification; }
/** Sets the line spacing of the TextEditor.
The default (and minimum) value is 1.0 and values > 1.0 will increase the line spacing as a
multiple of the line height e.g. for double-spacing call this method with an argument of 2.0.
@@ -712,7 +725,7 @@ private:
std::unique_ptr<Viewport> viewport;
TextHolderComponent* textHolder;
BorderSize<int> borderSize { 1, 1, 1, 3 };
Justification justification { Justification::left };
Justification justification { Justification::topLeft };
bool readOnly = false;
bool caretVisible = true;
@@ -728,6 +741,7 @@ private:
bool menuActive = false;
bool valueTextNeedsUpdating = false;
bool consumeEscAndReturnKeys = true;
bool underlineWhitespace = true;
UndoManager undoManager;
std::unique_ptr<CaretComponent> caret;
@@ -778,9 +792,12 @@ private:
int findWordBreakBefore (int position) const;
bool moveCaretWithTransaction (int newPos, bool selecting);
void drawContent (Graphics&);
void updateTextHolderSize();
void checkLayout();
void updateTextHolderSize (int, int);
void updateScrollbarVisibility (int, int);
float getWordWrapWidth() const;
float getJustificationWidth() const;
float getMaximumWidth() const;
float getMaximumHeight() const;
void timerCallbackInt();
void checkFocus();
void repaintText (Range<int>);


+ 14
- 14
source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp View File

@@ -40,9 +40,7 @@ CallOutBox::CallOutBox (Component& c, Rectangle<int> area, Component* const pare
else
{
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
updatePosition (area, Desktop::getInstance().getDisplays().findDisplayForRect (area).userArea);
addToDesktop (ComponentPeer::windowIsTemporary);
startTimer (100);
@@ -51,15 +49,14 @@ CallOutBox::CallOutBox (Component& c, Rectangle<int> area, Component* const pare
creationTime = Time::getCurrentTime();
}
CallOutBox::~CallOutBox() = default;
//==============================================================================
class CallOutBoxCallback : public ModalComponentManager::Callback,
private Timer
{
public:
CallOutBoxCallback (Component* c, const Rectangle<int>& area, Component* parent)
: content (c), callout (*c, area, parent)
CallOutBoxCallback (std::unique_ptr<Component> c, const Rectangle<int>& area, Component* parent)
: content (std::move (c)),
callout (*content, area, parent)
{
callout.setVisible (true);
callout.enterModalState (true, this);
@@ -80,11 +77,11 @@ public:
JUCE_DECLARE_NON_COPYABLE (CallOutBoxCallback)
};
CallOutBox& CallOutBox::launchAsynchronously (Component* content, Rectangle<int> area, Component* parent)
CallOutBox& CallOutBox::launchAsynchronously (std::unique_ptr<Component> content, Rectangle<int> area, Component* parent)
{
jassert (content != nullptr); // must be a valid content component!
return (new CallOutBoxCallback (content, area, parent))->callout;
return (new CallOutBoxCallback (std::move (content), area, parent))->callout;
}
//==============================================================================
@@ -99,7 +96,11 @@ int CallOutBox::getBorderSize() const noexcept
return jmax (getLookAndFeel().getCallOutBoxBorderSize (*this), (int) arrowSize);
}
void CallOutBox::lookAndFeelChanged() { resized(); repaint(); }
void CallOutBox::lookAndFeelChanged()
{
resized();
repaint();
}
void CallOutBox::paint (Graphics& g)
{
@@ -158,7 +159,7 @@ void CallOutBox::setDismissalMouseClicksAreAlwaysConsumed (bool b) noexcept
dismissalMouseClicksAreAlwaysConsumed = b;
}
enum { callOutBoxDismissCommandId = 0x4f83a04b };
static constexpr int callOutBoxDismissCommandId = 0x4f83a04b;
void CallOutBox::handleCommandMessage (int commandId)
{
@@ -193,9 +194,8 @@ void CallOutBox::updatePosition (const Rectangle<int>& newAreaToPointTo, const R
availableArea = newAreaToFitIn;
auto borderSpace = getBorderSize();
Rectangle<int> newBounds (content.getWidth() + borderSpace * 2,
content.getHeight() + borderSpace * 2);
auto newBounds = getLocalArea (&content, Rectangle<int> (content.getWidth() + borderSpace * 2,
content.getHeight() + borderSpace * 2));
auto hw = newBounds.getWidth() / 2;
auto hh = newBounds.getHeight() / 2;
@@ -250,7 +250,7 @@ void CallOutBox::refreshPath()
const float gap = 4.5f;
outline.addBubble (content.getBounds().toFloat().expanded (gap, gap),
outline.addBubble (getLocalArea (&content, content.getLocalBounds().toFloat()).expanded (gap, gap),
getLocalBounds().toFloat(),
targetPoint - getPosition().toFloat(),
getLookAndFeel().getCallOutBoxCornerSize (*this), arrowSize * 0.7f);


+ 5
- 9
source/modules/juce_gui_basics/windows/juce_CallOutBox.h View File

@@ -43,11 +43,12 @@ namespace juce
@code
void mouseUp (const MouseEvent&)
{
FoobarContentComp* content = new FoobarContentComp();
auto content = std::make_unique<FoobarContentComp>();
content->setSize (300, 300);
CallOutBox& myBox
= CallOutBox::launchAsynchronously (content, getScreenBounds(), nullptr);
auto& myBox = CallOutBox::launchAsynchronously (std::move (content),
getScreenBounds(),
nullptr);
}
@endcode
@@ -77,9 +78,6 @@ public:
Rectangle<int> areaToPointTo,
Component* parentComponent);
/** Destructor. */
~CallOutBox() override;
//==============================================================================
/** Changes the base width of the arrow. */
void setArrowSize (float newSize);
@@ -109,15 +107,13 @@ public:
@param contentComponent the component to display inside the call-out. This should
already have a size set (although the call-out will also
update itself when the component's size is changed later).
This component will be owned by the callout box and deleted
later when the box is dismissed.
@param areaToPointTo the area that the call-out's arrow should point towards. If
a parentComponent is supplied, then this is relative to that
parent; otherwise, it's a global screen coord.
@param parentComponent if not a nullptr, this is the component to add the call-out to.
If this is a nullptr, the call-out will be added to the desktop.
*/
static CallOutBox& launchAsynchronously (Component* contentComponent,
static CallOutBox& launchAsynchronously (std::unique_ptr<Component> contentComponent,
Rectangle<int> areaToPointTo,
Component* parentComponent);


+ 10
- 0
source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp View File

@@ -402,6 +402,16 @@ Rectangle<int> ComponentPeer::globalToLocal (const Rectangle<int>& screenPositio
return screenPosition.withPosition (globalToLocal (screenPosition.getPosition()));
}
Rectangle<float> ComponentPeer::localToGlobal (const Rectangle<float>& relativePosition)
{
return relativePosition.withPosition (localToGlobal (relativePosition.getPosition()));
}
Rectangle<float> ComponentPeer::globalToLocal (const Rectangle<float>& screenPosition)
{
return screenPosition.withPosition (globalToLocal (screenPosition.getPosition()));
}
Rectangle<int> ComponentPeer::getAreaCoveredBy (Component& subComponent) const
{
return ScalingHelpers::scaledScreenPosToUnscaled


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save