Browse Source

Update to JUCE 6.0.4

tags/v2.3.0-RC1
falkTX 5 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 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) 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; intData += destBytesPerSample;
} }
} }
@@ -43,7 +43,7 @@ void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest
for (int i = numSamples; --i >= 0;) for (int i = numSamples; --i >= 0;)
{ {
intData -= destBytesPerSample; 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) 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; intData += destBytesPerSample;
} }
} }
@@ -68,7 +68,7 @@ void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest
for (int i = numSamples; --i >= 0;) for (int i = numSamples; --i >= 0;)
{ {
intData -= destBytesPerSample; 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) 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; intData += destBytesPerSample;
} }
} }
@@ -143,7 +143,7 @@ void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest
for (int i = numSamples; --i >= 0;) for (int i = numSamples; --i >= 0;)
{ {
intData -= destBytesPerSample; 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) 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; intData += destBytesPerSample;
} }
} }
@@ -168,7 +168,7 @@ void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest
for (int i = numSamples; --i >= 0;) for (int i = numSamples; --i >= 0;)
{ {
intData -= destBytesPerSample; 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) for (int i = 0; i < numSamples; ++i)
{ {
*reinterpret_cast<float*> (d) = source[i];
*unalignedPointerCast<float*> (d) = source[i];
#if JUCE_BIG_ENDIAN #if JUCE_BIG_ENDIAN
*reinterpret_cast<uint32*> (d) = ByteOrder::swap (*reinterpret_cast<uint32*> (d));
*unalignedPointerCast<uint32*> (d) = ByteOrder::swap (*unalignedPointerCast<uint32*> (d));
#endif #endif
d += destBytesPerSample; d += destBytesPerSample;
@@ -199,10 +199,10 @@ void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* de
for (int i = 0; i < numSamples; ++i) for (int i = 0; i < numSamples; ++i)
{ {
*reinterpret_cast<float*> (d) = source[i];
*unalignedPointerCast<float*> (d) = source[i];
#if JUCE_LITTLE_ENDIAN #if JUCE_LITTLE_ENDIAN
*reinterpret_cast<uint32*> (d) = ByteOrder::swap (*reinterpret_cast<uint32*> (d));
*unalignedPointerCast<uint32*> (d) = ByteOrder::swap (*unalignedPointerCast<uint32*> (d));
#endif #endif
d += destBytesPerSample; d += destBytesPerSample;
@@ -219,7 +219,7 @@ void AudioDataConverters::convertInt16LEToFloat (const void* source, float* dest
{ {
for (int i = 0; i < numSamples; ++i) 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; intData += srcBytesPerSample;
} }
} }
@@ -230,7 +230,7 @@ void AudioDataConverters::convertInt16LEToFloat (const void* source, float* dest
for (int i = numSamples; --i >= 0;) for (int i = numSamples; --i >= 0;)
{ {
intData -= srcBytesPerSample; 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) 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; intData += srcBytesPerSample;
} }
} }
@@ -255,7 +255,7 @@ void AudioDataConverters::convertInt16BEToFloat (const void* source, float* dest
for (int i = numSamples; --i >= 0;) for (int i = numSamples; --i >= 0;)
{ {
intData -= srcBytesPerSample; 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) 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; intData += srcBytesPerSample;
} }
} }
@@ -330,7 +330,7 @@ void AudioDataConverters::convertInt32LEToFloat (const void* source, float* dest
for (int i = numSamples; --i >= 0;) for (int i = numSamples; --i >= 0;)
{ {
intData -= srcBytesPerSample; 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) 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; intData += srcBytesPerSample;
} }
} }
@@ -355,7 +355,7 @@ void AudioDataConverters::convertInt32BEToFloat (const void* source, float* dest
for (int i = numSamples; --i >= 0;) for (int i = numSamples; --i >= 0;)
{ {
intData -= srcBytesPerSample; 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) for (int i = 0; i < numSamples; ++i)
{ {
dest[i] = *reinterpret_cast<const float*> (s);
dest[i] = *unalignedPointerCast<const float*> (s);
#if JUCE_BIG_ENDIAN #if JUCE_BIG_ENDIAN
auto d = reinterpret_cast<uint32*> (dest + i);
auto d = unalignedPointerCast<uint32*> (dest + i);
*d = ByteOrder::swap (*d); *d = ByteOrder::swap (*d);
#endif #endif
@@ -383,10 +383,10 @@ void AudioDataConverters::convertFloat32BEToFloat (const void* source, float* de
for (int i = 0; i < numSamples; ++i) for (int i = 0; i < numSamples; ++i)
{ {
dest[i] = *reinterpret_cast<const float*> (s);
dest[i] = *unalignedPointerCast<const float*> (s);
#if JUCE_LITTLE_ENDIAN #if JUCE_LITTLE_ENDIAN
auto d = reinterpret_cast<uint32*> (dest + i);
auto d = unalignedPointerCast<uint32*> (dest + i);
*d = ByteOrder::swap (*d); *d = ByteOrder::swap (*d);
#endif #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 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) for (int j = 0; j < newNumChannels; ++j)
{ {
@@ -442,10 +442,10 @@ public:
{ {
allocatedBytes = newTotalBytes; allocatedBytes = newTotalBytes;
allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear); 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) for (int i = 0; i < newNumChannels; ++i)
{ {
@@ -1127,7 +1127,7 @@ private:
void allocateData() 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, static_assert (alignof (Type) <= detail::maxAlignment,
"AudioBuffer cannot hold types with alignment requirements larger than that guaranteed by malloc"); "AudioBuffer cannot hold types with alignment requirements larger than that guaranteed by malloc");
#endif #endif
@@ -1142,8 +1142,8 @@ private:
allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (Type) + channelListSize + 32; allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (Type) + channelListSize + 32;
allocatedData.malloc (allocatedBytes); 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) for (int i = 0; i < numChannels; ++i)
{ {
@@ -1167,7 +1167,7 @@ private:
else else
{ {
allocatedData.malloc (numChannels + 1, sizeof (Type*)); allocatedData.malloc (numChannels + 1, sizeof (Type*));
channels = reinterpret_cast<Type**> (allocatedData.get());
channels = unalignedPointerCast<Type**> (allocatedData.get());
} }
for (int i = 0; i < numChannels; ++i) 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 ID: juce_audio_basics
vendor: juce vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE audio and MIDI data classes name: JUCE audio and MIDI data classes
description: Classes for audio buffer manipulation, midi message handling, synthesis, etc. description: Classes for audio buffer manipulation, midi message handling, synthesis, etc.
website: http://www.juce.com/juce 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. */ /** Receives events from a MidiKeyboardState object. */
class Listener
class JUCE_API Listener
{ {
public: 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. /** Returns true if the message is an aftertouch event.
For aftertouch events, use the getNoteNumber() method to find out the key 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. getChannel() to find out the channel.
@see getAftertouchValue, getNoteNumber @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) 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_DirectSound());
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO());
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio());
@@ -544,6 +545,8 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup
else if (currentAudioDevice != nullptr) else if (currentAudioDevice != nullptr)
return {}; return {};
stopDevice();
if (getCurrentDeviceTypeObject() == nullptr if (getCurrentDeviceTypeObject() == nullptr
|| (newSetup.inputDeviceName.isEmpty() && newSetup.outputDeviceName.isEmpty())) || (newSetup.inputDeviceName.isEmpty() && newSetup.outputDeviceName.isEmpty()))
{ {
@@ -555,8 +558,6 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup
return {}; return {};
} }
stopDevice();
String error; String error;
if (currentSetup.inputDeviceName != newSetup.inputDeviceName 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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #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 #endif
} // namespace juce } // 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(); static AudioIODeviceType* createAudioIODeviceType_CoreAudio();
/** Creates an iOS device type if it's available on this platform, or returns null. */ /** Creates an iOS device type if it's available on this platform, or returns null. */
static AudioIODeviceType* createAudioIODeviceType_iOSAudio(); 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. */ /** Creates a DirectSound device type if it's available on this platform, or returns null. */
static AudioIODeviceType* createAudioIODeviceType_DirectSound(); static AudioIODeviceType* createAudioIODeviceType_DirectSound();
/** Creates an ASIO device type if it's available on this platform, or returns null. */ /** 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. */ /** Creates a Bela device type if it's available on this platform, or returns null. */
static AudioIODeviceType* createAudioIODeviceType_Bela(); 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: protected:
explicit AudioIODeviceType (const String& typeName); 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 "juce_audio_devices.h"
#include "native/juce_MidiDataConcatenator.h"
//============================================================================== //==============================================================================
#if JUCE_MAC #if JUCE_MAC
#define Point CarbonDummyPointName #define Point CarbonDummyPointName
@@ -55,6 +57,9 @@
#undef Point #undef Point
#undef Component #undef Component
#include "native/juce_mac_CoreAudio.cpp"
#include "native/juce_mac_CoreMidi.cpp"
#elif JUCE_IOS #elif JUCE_IOS
#import <AudioToolbox/AudioToolbox.h> #import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h> #import <AVFoundation/AVFoundation.h>
@@ -64,13 +69,21 @@
#import <CoreMIDI/MIDINetworkSession.h> #import <CoreMIDI/MIDINetworkSession.h>
#endif #endif
#include "native/juce_ios_Audio.cpp"
#include "native/juce_mac_CoreMidi.cpp"
//============================================================================== //==============================================================================
#elif JUCE_WINDOWS #elif JUCE_WINDOWS
#if JUCE_WASAPI #if JUCE_WASAPI
#include <mmreg.h> #include <mmreg.h>
#include "native/juce_win32_WASAPI.cpp"
#endif #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 /* 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 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 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 JUCE_END_IGNORE_WARNINGS_MSVC
#endif #endif
#include "native/juce_win32_Midi.cpp"
#if JUCE_ASIO #if JUCE_ASIO
/* This is very frustrating - we only need to use a handful of definitions from /* 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 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). needed - so to simplify things, you could just copy these into your JUCE directory).
*/ */
#include <iasiodrv.h> #include <iasiodrv.h>
#include "native/juce_win32_ASIO.cpp"
#endif #endif
//============================================================================== //==============================================================================
@@ -128,6 +144,7 @@
just set the JUCE_ALSA flag to 0. just set the JUCE_ALSA flag to 0.
*/ */
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "native/juce_linux_ALSA.cpp"
#endif #endif
#if JUCE_JACK #if JUCE_JACK
@@ -140,6 +157,7 @@
JUCE with low latency audio support, just set the JUCE_JACK flag to 0. JUCE with low latency audio support, just set the JUCE_JACK flag to 0.
*/ */
#include <jack/jack.h> #include <jack/jack.h>
#include "native/juce_linux_JackAudio.cpp"
#endif #endif
#if JUCE_BELA #if JUCE_BELA
@@ -149,89 +167,18 @@
*/ */
#include <Bela.h> #include <Bela.h>
#include <Midi.h> #include <Midi.h>
#include "native/juce_linux_Bela.cpp"
#endif #endif
#undef SIZEOF #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" #include "native/juce_linux_Midi.cpp"
#endif #endif
//============================================================================== //==============================================================================
#elif JUCE_ANDROID #elif JUCE_ANDROID
#include "native/juce_android_Audio.cpp" #include "native/juce_android_Audio.cpp"
#include "native/juce_android_Midi.cpp" #include "native/juce_android_Midi.cpp"
@@ -239,10 +186,25 @@
#include "native/juce_android_HighPerformanceAudioHelpers.h" #include "native/juce_android_HighPerformanceAudioHelpers.h"
#if JUCE_USE_ANDROID_OPENSLES #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" #include "native/juce_android_OpenSL.cpp"
#endif #endif
#if JUCE_USE_ANDROID_OBOE #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" #include "native/juce_android_Oboe.cpp"
#endif #endif
#endif #endif
@@ -259,3 +221,11 @@ namespace juce
bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; } bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; }
} }
#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"

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

@@ -32,7 +32,7 @@
ID: juce_audio_devices ID: juce_audio_devices
vendor: juce vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE audio and MIDI I/O device classes name: JUCE audio and MIDI I/O device classes
description: Classes to play and record from audio and MIDI I/O devices description: Classes to play and record from audio and MIDI I/O devices
website: http://www.juce.com/juce website: http://www.juce.com/juce
@@ -88,21 +88,12 @@
#endif #endif
/** Config: JUCE_WASAPI /** 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 #ifndef JUCE_WASAPI
#define JUCE_WASAPI 1 #define JUCE_WASAPI 1
#endif #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 /** Config: JUCE_DIRECTSOUND
Enables DirectSound audio (MS Windows only). Enables DirectSound audio (MS Windows only).
*/ */
@@ -174,6 +165,22 @@
//============================================================================== //==============================================================================
#include "midi_io/juce_MidiDevices.h" #include "midi_io/juce_MidiDevices.h"
#include "midi_io/juce_MidiMessageCollector.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_AudioIODevice.h"
#include "audio_io/juce_AudioIODeviceType.h" #include "audio_io/juce_AudioIODeviceType.h"
#include "audio_io/juce_SystemAudioVolume.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. */ /** Deprecated. */
static std::unique_ptr<MidiInput> openDevice (int, MidiInputCallback*); static std::unique_ptr<MidiInput> openDevice (int, MidiInputCallback*);
/** @internal */
class Pimpl;
private: private:
//============================================================================== //==============================================================================
explicit MidiInput (const String&, const String&); explicit MidiInput (const String&, const String&);
MidiDeviceInfo deviceInfo; MidiDeviceInfo deviceInfo;
void* internal = nullptr;
std::unique_ptr<Pimpl> internal;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput)
}; };
@@ -350,6 +354,9 @@ public:
/** Deprecated. */ /** Deprecated. */
static std::unique_ptr<MidiOutput> openDevice (int); static std::unique_ptr<MidiOutput> openDevice (int);
/** @internal */
class Pimpl;
private: private:
//============================================================================== //==============================================================================
struct PendingMessage struct PendingMessage
@@ -368,7 +375,9 @@ private:
void run() override; void run() override;
MidiDeviceInfo deviceInfo; MidiDeviceInfo deviceInfo;
void* internal = nullptr;
std::unique_ptr<Pimpl> internal;
CriticalSection lock; CriticalSection lock;
PendingMessage* firstMessage = nullptr; 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 isOboeAvailable();
extern bool isOpenSLAvailable(); 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 } // 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 #undef JNI_CLASS_MEMBERS
//============================================================================== //==============================================================================
class AndroidMidiInput
class MidiInput::Pimpl
{ {
public: 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), : juceMidiInput (midiInput), callback (midiInputCallback), midiConcatenator (2048),
javaMidiDevice (LocalRef<jobject>(getEnv()->CallObjectMethod (deviceManager, MidiDeviceManager.openMidiInputPortWithID, javaMidiDevice (LocalRef<jobject>(getEnv()->CallObjectMethod (deviceManager, MidiDeviceManager.openMidiInputPortWithID,
(jint) deviceID, (jlong) this))) (jint) deviceID, (jlong) this)))
{ {
} }
~AndroidMidiInput()
~Pimpl()
{ {
if (jobject d = javaMidiDevice.get()) if (jobject d = javaMidiDevice.get())
{ {
@@ -416,7 +416,7 @@ public:
static void handleReceive (JNIEnv*, jobject, jlong host, jbyteArray byteArray, static void handleReceive (JNIEnv*, jobject, jlong host, jbyteArray byteArray,
jint offset, jint len, jlong timestamp) jint offset, jint len, jlong timestamp)
{ {
auto* myself = reinterpret_cast<AndroidMidiInput*> (host);
auto* myself = reinterpret_cast<Pimpl*> (host);
myself->handleMidi (byteArray, offset, len, timestamp); myself->handleMidi (byteArray, offset, len, timestamp);
} }
@@ -429,15 +429,15 @@ private:
}; };
//============================================================================== //==============================================================================
class AndroidMidiOutput
class MidiOutput::Pimpl
{ {
public: public:
AndroidMidiOutput (const LocalRef<jobject>& midiDevice)
Pimpl (const LocalRef<jobject>& midiDevice)
: javaMidiDevice (midiDevice) : javaMidiDevice (midiDevice)
{ {
} }
~AndroidMidiOutput()
~Pimpl()
{ {
if (jobject d = javaMidiDevice.get()) if (jobject d = javaMidiDevice.get())
{ {
@@ -468,7 +468,7 @@ private:
//============================================================================== //==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ #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) DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiInputPort, "com/rmsl/juce/JuceMidiSupport$JuceMidiInputPort", 23)
#undef JNI_CLASS_MEMBERS #undef JNI_CLASS_MEMBERS
@@ -509,11 +509,11 @@ public:
return {}; 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()) 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()) if (androidMidiInput->isOpen())
return androidMidiInput.release(); return androidMidiInput.release();
@@ -522,11 +522,11 @@ public:
return nullptr; return nullptr;
} }
AndroidMidiOutput* openMidiOutputPortWithID (int deviceID)
MidiOutput::Pimpl* openMidiOutputPortWithID (int deviceID)
{ {
if (auto dm = deviceManager.get()) if (auto dm = deviceManager.get())
if (auto javaMidiPort = getEnv()->CallObjectMethod (dm, MidiDeviceManager.openMidiOutputPortWithID, (jint) deviceID)) 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; 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)) if (auto* port = manager.openMidiInputPortWithID (deviceIdentifier.getIntValue(), midiInput.get(), callback))
{ {
midiInput->internal = port;
midiInput->internal.reset (port);
midiInput->setName (port->getName()); midiInput->setName (port->getName());
return midiInput; 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() void MidiInput::start()
{ {
if (auto* mi = reinterpret_cast<AndroidMidiInput*> (internal))
if (auto* mi = internal.get())
mi->start(); mi->start();
} }
void MidiInput::stop() void MidiInput::stop()
{ {
if (auto* mi = reinterpret_cast<AndroidMidiInput*> (internal))
if (auto* mi = internal.get())
mi->stop(); mi->stop();
} }
@@ -646,7 +643,7 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifi
if (auto* port = manager.openMidiOutputPortWithID (deviceIdentifier.getIntValue())) if (auto* port = manager.openMidiOutputPortWithID (deviceIdentifier.getIntValue()))
{ {
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput ({}, deviceIdentifier)); std::unique_ptr<MidiOutput> midiOutput (new MidiOutput ({}, deviceIdentifier));
midiOutput->internal = port;
midiOutput->internal.reset (port);
midiOutput->setName (port->getName()); midiOutput->setName (port->getName());
return midiOutput; return midiOutput;
@@ -681,13 +678,11 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (int index)
MidiOutput::~MidiOutput() MidiOutput::~MidiOutput()
{ {
stopBackgroundThread(); stopBackgroundThread();
delete reinterpret_cast<AndroidMidiOutput*> (internal);
} }
void MidiOutput::sendMessageNow (const MidiMessage& message) void MidiOutput::sendMessageNow (const MidiMessage& message)
{ {
if (auto* androidMidi = reinterpret_cast<AndroidMidiOutput*>(internal))
if (auto* androidMidi = internal.get())
{ {
auto* env = getEnv(); auto* env = getEnv();
auto messageSize = message.getRawDataSize(); 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 getChannelCountsMethod = env->GetMethodID (deviceClass, "getChannelCounts", "()[I");
jmethodID isSourceMethod = env->GetMethodID (deviceClass, "isSource", "()Z"); 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 jSampleRates = LocalRef<jintArray> ((jintArray) env->CallObjectMethod (device, getSampleRatesMethod));
auto sampleRates = jintArrayToJuceArray (jSampleRates); auto sampleRates = jintArrayToJuceArray (jSampleRates);
@@ -1222,42 +1226,42 @@ public:
auto channelCounts = jintArrayToJuceArray (jChannelCounts); auto channelCounts = jintArrayToJuceArray (jChannelCounts);
int numChannels = channelCounts.isEmpty() ? -1 : channelCounts.getLast(); int numChannels = channelCounts.isEmpty() ? -1 : channelCounts.getLast();
bool isInput = env->CallBooleanMethod (device, isSourceMethod);
auto isInput = env->CallBooleanMethod (device, isSourceMethod);
auto& devices = isInput ? inputDevices : outputDevices; auto& devices = isInput ? inputDevices : outputDevices;
devices.add ({ name, id, sampleRates, numChannels }); devices.add ({ name, id, sampleRates, numChannels });
} }
static const char* deviceTypeToString (int type)
static String deviceTypeToString (int type)
{ {
switch (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"; const char* const OboeAudioIODevice::oboeTypeName = "Android Oboe";
//==============================================================================
bool isOboeAvailable() { return OboeAudioIODeviceType::isOboeAvailable(); } bool isOboeAvailable() { return OboeAudioIODeviceType::isOboeAvailable(); }
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe()
{
return isOboeAvailable() ? new OboeAudioIODeviceType() : nullptr;
}
//============================================================================== //==============================================================================
class OboeRealtimeThread : private oboe::AudioStreamCallback 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); (*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() = default;
ControlBlock (SLObjectItf o) : ptr (o) {} ControlBlock (SLObjectItf o) : ptr (o) {}
std::unique_ptr<const SLObjectItf_* const> ptr;
std::unique_ptr<const SLObjectItf_* const, SLObjectItfFree> ptr;
}; };
ReferenceCountedObjectPtr<ControlBlock> cb; ReferenceCountedObjectPtr<ControlBlock> cb;
@@ -1121,11 +1120,6 @@ const char* const OpenSLAudioIODevice::openSLTypeName = "Android OpenSL";
//============================================================================== //==============================================================================
bool isOpenSLAvailable() { return OpenSLAudioDeviceType::isOpenSLAvailable(); } bool isOpenSLAvailable() { return OpenSLAudioDeviceType::isOpenSLAvailable(); }
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES()
{
return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr;
}
//============================================================================== //==============================================================================
class SLRealtimeThread class SLRealtimeThread
{ {


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

@@ -1431,12 +1431,6 @@ void iOSAudioIODeviceType::handleAsyncUpdate()
callDeviceChangeListeners(); callDeviceChangeListeners();
} }
//==============================================================================
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio()
{
return new iOSAudioIODeviceType();
}
//============================================================================== //==============================================================================
AudioSessionHolder::AudioSessionHolder() { nativeSession = [[iOSAudioSessionNative alloc] init: this]; } AudioSessionHolder::AudioSessionHolder() { nativeSession = [[iOSAudioSessionNative alloc] init: this]; }
AudioSessionHolder::~AudioSessionHolder() { [nativeSession release]; } 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"); return new ALSAAudioIODeviceType (false, "ALSA");
} }
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA()
{
return createAudioIODeviceType_ALSA_PCMDevices();
}
} // namespace juce } // 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: 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) : midiInput (input), midiPort (port), midiCallback (callback)
{ {
jassert (midiCallback != nullptr); jassert (midiCallback != nullptr);
@@ -38,7 +38,7 @@ public:
buffer.resize (32); buffer.resize (32);
} }
~BelaMidiInput()
~Pimpl()
{ {
stop(); stop();
midiInputs.removeAllInstancesOf (this); midiInputs.removeAllInstancesOf (this);
@@ -76,7 +76,7 @@ public:
} }
if (receivedBytes > 0) if (receivedBytes > 0)
pushMidiData (receivedBytes);
pushMidiData ((int) receivedBytes);
} }
static Array<MidiDeviceInfo> getDevices (bool input) static Array<MidiDeviceInfo> getDevices (bool input)
@@ -141,7 +141,7 @@ private:
snd_rawmidi_info_t* info; snd_rawmidi_info_t* info;
snd_rawmidi_info_alloca (&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_info_set_stream (info, input ? SND_RAWMIDI_STREAM_INPUT
: SND_RAWMIDI_STREAM_OUTPUT); : SND_RAWMIDI_STREAM_OUTPUT);
@@ -173,10 +173,10 @@ private:
Midi midi; Midi midi;
MidiDataConcatenator concatenator { 512 }; 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; } String getLastError() override { return lastError; }
//============================================================================== //==============================================================================
int getCurrentBufferSizeSamples() override { return actualBufferSize; }
int getCurrentBufferSizeSamples() override { return (int) actualBufferSize; }
double getCurrentSampleRate() override { return 44100.0; } double getCurrentSampleRate() override { return 44100.0; }
int getCurrentBitDepth() override { return 16; } int getCurrentBitDepth() override { return 16; }
BigInteger getActiveOutputChannels() const override { BigInteger b; b.setRange (0, actualNumberOfOutputs, true); return b; } BigInteger getActiveOutputChannels() const override { BigInteger b; b.setRange (0, actualNumberOfOutputs, true); return b; }
@@ -384,8 +384,8 @@ private:
bool setup (BelaContext& context) bool setup (BelaContext& context)
{ {
actualBufferSize = context.audioFrames; 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; isBelaOpen = true;
firstCallback = true; firstCallback = true;
@@ -405,7 +405,7 @@ private:
ScopedLock lock (callbackLock); ScopedLock lock (callbackLock);
// Check for and process and midi // Check for and process and midi
for (auto midiInput : BelaMidiInput::midiInputs)
for (auto midiInput : MidiInput::Pimpl::midiInputs)
midiInput->poll(); midiInput->poll();
if (callback != nullptr) if (callback != nullptr)
@@ -413,27 +413,29 @@ private:
jassert (context.audioFrames <= actualBufferSize); jassert (context.audioFrames <= actualBufferSize);
jassert ((context.flags & BELA_FLAG_INTERLEAVED) == 0); jassert ((context.flags & BELA_FLAG_INTERLEAVED) == 0);
using Frames = decltype (context.audioFrames);
// Setup channelInBuffers // Setup channelInBuffers
for (int ch = 0; ch < actualNumberOfInputs; ++ch) for (int ch = 0; ch < actualNumberOfInputs; ++ch)
{ {
if (ch < analogChannelStart) if (ch < analogChannelStart)
channelInBuffer[ch] = &context.audioIn[ch * context.audioFrames];
channelInBuffer[ch] = &context.audioIn[(Frames) ch * context.audioFrames];
else else
channelInBuffer[ch] = &context.analogIn[(ch - analogChannelStart) * context.analogFrames];
channelInBuffer[ch] = &context.analogIn[(Frames) (ch - analogChannelStart) * context.analogFrames];
} }
// Setup channelOutBuffers // Setup channelOutBuffers
for (int ch = 0; ch < actualNumberOfOutputs; ++ch) for (int ch = 0; ch < actualNumberOfOutputs; ++ch)
{ {
if (ch < analogChannelStart) if (ch < analogChannelStart)
channelOutBuffer[ch] = &context.audioOut[ch * context.audioFrames];
channelOutBuffer[ch] = &context.audioOut[(Frames) ch * context.audioFrames];
else else
channelOutBuffer[ch] = &context.analogOut[(ch - analogChannelStart) * context.audioFrames];
channelOutBuffer[ch] = &context.analogOut[(Frames) (ch - analogChannelStart) * context.audioFrames];
} }
callback->audioDeviceIOCallback (channelInBuffer.getData(), actualNumberOfInputs, callback->audioDeviceIOCallback (channelInBuffer.getData(), actualNumberOfInputs,
channelOutBuffer.getData(), actualNumberOfOutputs, channelOutBuffer.getData(), actualNumberOfOutputs,
context.audioFrames);
(int) context.audioFrames);
} }
} }
@@ -521,25 +523,19 @@ struct BelaAudioIODeviceType : public AudioIODeviceType
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODeviceType) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODeviceType)
}; };
//==============================================================================
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela()
{
return new BelaAudioIODeviceType();
}
//============================================================================== //==============================================================================
MidiInput::MidiInput (const String& deviceName, const String& deviceID) MidiInput::MidiInput (const String& deviceName, const String& deviceID)
: deviceInfo (deviceName, 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() Array<MidiDeviceInfo> MidiInput::getAvailableDevices()
{ {
return BelaMidiInput::getDevices (true);
return Pimpl::getDevices (true);
} }
MidiDeviceInfo MidiInput::getDefaultDevice() MidiDeviceInfo MidiInput::getDefaultDevice()
@@ -553,7 +549,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier
return {}; return {};
std::unique_ptr<MidiInput> midiInput (new MidiInput (deviceIdentifier, deviceIdentifier)); 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; return midiInput;
} }
@@ -587,7 +583,8 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (int index, MidiInputCallback*
//============================================================================== //==============================================================================
// TODO: Add Bela MidiOutput support // TODO: Add Bela MidiOutput support
MidiOutput::~MidiOutput() {}
class MidiOutput::Pimpl {};
MidiOutput::~MidiOutput() = default;
void MidiOutput::sendMessageNow (const MidiMessage&) {} void MidiOutput::sendMessageNow (const MidiMessage&) {}
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices() { return {}; } Array<MidiDeviceInfo> MidiOutput::getAvailableDevices() { return {}; }
MidiDeviceInfo MidiOutput::getDefaultDevice() { 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) \ #define JUCE_DECL_JACK_FUNCTION(return_type, fn_name, argument_types, arguments) \
return_type fn_name argument_types \ return_type fn_name argument_types \
{ \ { \
using ReturnType = return_type; \
typedef return_type (*fn_type) argument_types; \ typedef return_type (*fn_type) argument_types; \
static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \ 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) \ #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; \ typedef void (*fn_type) argument_types; \
static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \ static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \
jassert (fn != nullptr); \
if (fn != nullptr) (*fn) arguments; \ 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 #if JUCE_DEBUG
#define JACK_LOGGING_ENABLED 1 #define JACK_LOGGING_ENABLED 1
@@ -115,56 +122,56 @@ namespace
struct JackPortIterator struct JackPortIterator
{ {
JackPortIterator (jack_client_t* const client, const bool forInput) JackPortIterator (jack_client_t* const client, const bool forInput)
: ports (nullptr), index (-1)
{ {
if (client != nullptr) 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() bool next()
{ {
if (ports == nullptr || ports [index + 1] == nullptr)
if (ports == nullptr || ports.get()[index + 1] == nullptr)
return false; return false;
name = CharPointer_UTF8 (ports[++index]);
clientName = name.upToFirstOccurrenceOf (":", false, false);
name = CharPointer_UTF8 (ports.get()[++index]);
return true; 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 name;
String clientName;
}; };
class JackAudioIODeviceType;
static Array<JackAudioIODeviceType*> activeDeviceTypes;
//============================================================================== //==============================================================================
class JackAudioIODevice : public AudioIODevice class JackAudioIODevice : public AudioIODevice
{ {
public: 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); client = juce::jack_client_open (JUCE_JACK_CLIENT_NAME, JackNoStartServer, &status);
if (client == nullptr) if (client == nullptr)
@@ -179,10 +186,10 @@ public:
const StringArray inputChannels (getInputChannelNames()); const StringArray inputChannels (getInputChannelNames());
for (int i = 0; i < inputChannels.size(); ++i) 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)); JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0));
} }
@@ -190,10 +197,10 @@ public:
const StringArray outputChannels (getOutputChannelNames()); const StringArray outputChannels (getOutputChannelNames());
for (int i = 0; i < outputChannels.size(); ++i) 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)); JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0));
} }
@@ -202,7 +209,7 @@ public:
} }
} }
~JackAudioIODevice()
~JackAudioIODevice() override
{ {
close(); close();
if (client != nullptr) if (client != nullptr)
@@ -212,19 +219,19 @@ public:
} }
} }
StringArray getChannelNames (bool forInput) const
StringArray getChannelNames (const String& clientName, bool forInput) const
{ {
StringArray names; StringArray names;
for (JackPortIterator i (client, forInput); i.next();) 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; 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 Array<double> getAvailableSampleRates() override
{ {
@@ -241,15 +248,29 @@ public:
Array<int> sizes; Array<int> sizes;
if (client != nullptr) if (client != nullptr)
sizes.add (juce::jack_get_buffer_size (client));
sizes.add (static_cast<int> (juce::jack_get_buffer_size (client)));
return sizes; return sizes;
} }
int getDefaultBufferSize() override { return getCurrentBufferSizeSamples(); } 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, String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
double /* sampleRate */, int /* bufferSizeSamples */) override double /* sampleRate */, int /* bufferSizeSamples */) override
@@ -263,38 +284,55 @@ public:
lastError.clear(); lastError.clear();
close(); close();
xruns = 0;
xruns.store (0, std::memory_order_relaxed);
juce::jack_set_process_callback (client, processCallback, this); juce::jack_set_process_callback (client, processCallback, this);
juce::jack_set_port_connect_callback (client, portConnectCallback, this); juce::jack_set_port_connect_callback (client, portConnectCallback, this);
juce::jack_on_shutdown (client, shutdownCallback, 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_set_xrun_callback (client, xrunCallback, this);
juce::jack_activate (client); juce::jack_activate (client);
deviceIsOpen = true; deviceIsOpen = true;
if (! inputChannels.isZero()) 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()) 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(); updateActivePorts();
@@ -308,12 +346,15 @@ public:
if (client != nullptr) 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_xrun_callback (client, xrunCallback, nullptr);
juce::jack_set_process_callback (client, processCallback, nullptr); juce::jack_set_process_callback (client, processCallback, nullptr);
juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr); juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr);
juce::jack_on_shutdown (client, shutdownCallback, nullptr); juce::jack_on_shutdown (client, shutdownCallback, nullptr);
juce::jack_on_info_shutdown (client, infoShutdownCallback, nullptr);
} }
deviceIsOpen = false; deviceIsOpen = false;
@@ -347,7 +388,7 @@ public:
bool isPlaying() override { return callback != nullptr; } bool isPlaying() override { return callback != nullptr; }
int getCurrentBitDepth() override { return 32; } int getCurrentBitDepth() override { return 32; }
String getLastError() override { return lastError; } 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 getActiveOutputChannels() const override { return activeOutputChannels; }
BigInteger getActiveInputChannels() const override { return activeInputChannels; } BigInteger getActiveInputChannels() const override { return activeInputChannels; }
@@ -357,7 +398,7 @@ public:
int latency = 0; int latency = 0;
for (int i = 0; i < outputPorts.size(); i++) 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; return latency;
} }
@@ -367,14 +408,36 @@ public:
int latency = 0; int latency = 0;
for (int i = 0; i < inputPorts.size(); i++) 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; return latency;
} }
String inputId, outputId;
String inputName, outputName;
private: 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) void process (const int numSamples)
{ {
int numActiveInChans = 0, numActiveOutChans = 0; int numActiveInChans = 0, numActiveOutChans = 0;
@@ -382,17 +445,17 @@ private:
for (int i = 0; i < totalNumberOfInputChannels; ++i) for (int i = 0; i < totalNumberOfInputChannels; ++i)
{ {
if (activeInputChannels[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) for (int i = 0; i < totalNumberOfOutputChannels; ++i)
{ {
if (activeOutputChannels[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); const ScopedLock sl (callbackLock);
@@ -406,14 +469,14 @@ private:
else else
{ {
for (int i = 0; i < numActiveOutChans; ++i) 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) static int processCallback (jack_nframes_t nframes, void* callbackArgument)
{ {
if (callbackArgument != nullptr) if (callbackArgument != nullptr)
((JackAudioIODevice*) callbackArgument)->process (nframes);
((JackAudioIODevice*) callbackArgument)->process (static_cast<int> (nframes));
return 0; return 0;
} }
@@ -431,11 +494,11 @@ private:
BigInteger newOutputChannels, newInputChannels; BigInteger newOutputChannels, newInputChannels;
for (int i = 0; i < outputPorts.size(); ++i) 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); newOutputChannels.setBit (i);
for (int i = 0; i < inputPorts.size(); ++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); newInputChannels.setBit (i);
if (newOutputChannels != activeOutputChannels if (newOutputChannels != activeOutputChannels
@@ -451,14 +514,15 @@ private:
if (oldCallback != nullptr) if (oldCallback != nullptr)
start (oldCallback); start (oldCallback);
sendDeviceChangedCallback();
if (notifyChannelsChanged != nullptr)
notifyChannelsChanged();
} }
} }
static void portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg) static void portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg)
{ {
if (JackAudioIODevice* device = static_cast<JackAudioIODevice*> (arg)) if (JackAudioIODevice* device = static_cast<JackAudioIODevice*> (arg))
device->updateActivePorts();
device->mainThreadDispatcher.updateActivePorts();
} }
static void threadInitCallback (void* /* callbackArgument */) 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) static void errorCallback (const char* msg)
{ {
JUCE_JACK_LOG ("JackAudioIODevice::errorCallback " + String (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; String lastError;
AudioIODeviceCallback* callback;
AudioIODeviceCallback* callback = nullptr;
CriticalSection callbackLock; CriticalSection callbackLock;
HeapBlock<float*> inChans, outChans; 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; BigInteger activeInputChannels, activeOutputChannels;
int xruns;
};
std::atomic<int> xruns { 0 };
std::function<void()> notifyChannelsChanged;
MainThreadDispatcher mainThreadDispatcher { *this };
};
//============================================================================== //==============================================================================
class JackAudioIODeviceType;
class JackAudioIODeviceType : public AudioIODeviceType class JackAudioIODeviceType : public AudioIODeviceType
{ {
public: public:
JackAudioIODeviceType() JackAudioIODeviceType()
: AudioIODeviceType ("JACK"),
hasScanned (false)
{
activeDeviceTypes.add (this);
}
~JackAudioIODeviceType()
{
activeDeviceTypes.removeFirstMatchingValue (this);
}
: AudioIODeviceType ("JACK")
{}
void scanForDevices() void scanForDevices()
{ {
hasScanned = true; hasScanned = true;
inputNames.clear(); inputNames.clear();
inputIds.clear();
outputNames.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.0", RTLD_LAZY);
if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY); if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY);
if (juce_libjackHandle == nullptr) return; if (juce_libjackHandle == nullptr) return;
jack_status_t status;
jack_status_t status = {};
// open a dummy client // 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 // scan for output devices
for (JackPortIterator i (client, false); i.next();) 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 // scan for input devices
for (JackPortIterator i (client, true); i.next();) 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); juce::jack_client_close (client);
} }
@@ -580,8 +639,8 @@ public:
jassert (hasScanned); // need to call scanForDevices() before doing this jassert (hasScanned); // need to call scanForDevices() before doing this
if (JackAudioIODevice* d = dynamic_cast<JackAudioIODevice*> (device)) 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; return -1;
} }
@@ -595,34 +654,17 @@ public:
const int outputIndex = outputNames.indexOf (outputDeviceName); const int outputIndex = outputNames.indexOf (outputDeviceName);
if (inputIndex >= 0 || outputIndex >= 0) 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; return nullptr;
} }
void portConnectionChange() { callDeviceChangeListeners(); }
private: private:
StringArray inputNames, outputNames, inputIds, outputIds;
bool hasScanned;
StringArray inputNames, outputNames;
bool hasScanned = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType) 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 } // 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 #if JUCE_ALSA
//==============================================================================
namespace
{
//============================================================================== //==============================================================================
class AlsaClient : public ReferenceCountedObject class AlsaClient : public ReferenceCountedObject
{ {
@@ -453,9 +449,23 @@ static AlsaClient::Port* iterateMidiDevices (bool forInput,
return port; 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> MidiInput::getAvailableDevices()
{ {
Array<MidiDeviceInfo> devices; 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)); std::unique_ptr<MidiInput> midiInput (new MidiInput (port->portName, deviceIdentifier));
port->setupInput (midiInput.get(), callback); port->setupInput (midiInput.get(), callback);
midiInput->internal = port;
midiInput->internal = std::make_unique<Pimpl> (port);
return midiInput; 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))); std::unique_ptr<MidiInput> midiInput (new MidiInput (deviceName, getFormattedPortIdentifier (client->getId(), port->portId)));
port->setupInput (midiInput.get(), callback); port->setupInput (midiInput.get(), callback);
midiInput->internal = port;
midiInput->internal = std::make_unique<Pimpl> (port);
return midiInput; return midiInput;
} }
@@ -536,20 +546,25 @@ MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier)
MidiInput::~MidiInput() MidiInput::~MidiInput()
{ {
stop(); stop();
AlsaClient::getInstance()->deletePort (static_cast<AlsaClient::Port*> (internal));
} }
void MidiInput::start() void MidiInput::start()
{ {
static_cast<AlsaClient::Port*> (internal)->enableCallback (true);
internal->ptr->enableCallback (true);
} }
void MidiInput::stop() 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> MidiOutput::getAvailableDevices()
{ {
Array<MidiDeviceInfo> devices; 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)); std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (port->portName, deviceIdentifier));
port->setupOutput(); port->setupOutput();
midiOutput->internal = port;
midiOutput->internal = std::make_unique<Pimpl> (port);
return midiOutput; 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))); std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (deviceName, getFormattedPortIdentifier (client->getId(), port->portId)));
port->setupOutput(); port->setupOutput();
midiOutput->internal = port;
midiOutput->internal = std::make_unique<Pimpl> (port);
return midiOutput; return midiOutput;
} }
@@ -623,17 +638,18 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (int index)
MidiOutput::~MidiOutput() MidiOutput::~MidiOutput()
{ {
stopBackgroundThread(); stopBackgroundThread();
AlsaClient::getInstance()->deletePort (static_cast<AlsaClient::Port*> (internal));
} }
void MidiOutput::sendMessageNow (const MidiMessage& message) void MidiOutput::sendMessageNow (const MidiMessage& message)
{ {
static_cast<AlsaClient::Port*> (internal)->sendMessageNow (message);
internal->ptr->sendMessageNow (message);
} }
//============================================================================== //==============================================================================
#else #else
class MidiInput::Pimpl {};
// (These are just stub functions if ALSA is unavailable...) // (These are just stub functions if ALSA is unavailable...)
MidiInput::MidiInput (const String& deviceName, const String& deviceID) MidiInput::MidiInput (const String& deviceName, const String& deviceID)
: deviceInfo (deviceName, deviceID) : deviceInfo (deviceName, deviceID)
@@ -651,6 +667,8 @@ StringArray MidiInput::getDevices()
int MidiInput::getDefaultDeviceIndex() { return 0;} int MidiInput::getDefaultDeviceIndex() { return 0;}
std::unique_ptr<MidiInput> MidiInput::openDevice (int, MidiInputCallback*) { return {}; } std::unique_ptr<MidiInput> MidiInput::openDevice (int, MidiInputCallback*) { return {}; }
class MidiOutput::Pimpl {};
MidiOutput::~MidiOutput() {} MidiOutput::~MidiOutput() {}
void MidiOutput::sendMessageNow (const MidiMessage&) {} void MidiOutput::sendMessageNow (const MidiMessage&) {}
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices() { return {}; } 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 #undef JUCE_COREAUDIOLOG
} // namespace juce } // 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() 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))) if (CHECK_ERROR (MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &cfName.cfString)))
{ {
MIDIPortRef port; 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))) 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)); std::unique_ptr<MidiInput> midiInput (new MidiInput (endpointInfo.name, endpointInfo.identifier));
mpc->input = midiInput.get(); mpc->input = midiInput.get();
midiInput->internal = mpc.get();
auto* ptr = mpc.get();
midiInput->internal = std::move (mpc);
const ScopedLock sl (callbackLock); const ScopedLock sl (callbackLock);
activeCallbacks.add (mpc.release());
activeCallbacks.add (ptr);
return midiInput; return midiInput;
} }
@@ -462,7 +469,7 @@ std::unique_ptr<MidiInput> MidiInput::createNewDevice (const String& deviceName,
if (auto client = getGlobalMidiClient()) if (auto client = getGlobalMidiClient())
{ {
auto mpc = std::make_unique<MidiPortAndCallback> (*callback);
auto mpc = std::make_unique<Pimpl> (*callback);
mpc->active = false; mpc->active = false;
MIDIEndpointRef endpoint; 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))); std::unique_ptr<MidiInput> midiInput (new MidiInput (deviceName, String (deviceIdentifier)));
mpc->input = midiInput.get(); mpc->input = midiInput.get();
midiInput->internal = mpc.get();
auto* ptr = mpc.get();
midiInput->internal = std::move (mpc);
const ScopedLock sl (callbackLock); const ScopedLock sl (callbackLock);
activeCallbacks.add (mpc.release());
activeCallbacks.add (ptr);
return midiInput; 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() void MidiInput::start()
{ {
const ScopedLock sl (CoreMidiHelpers::callbackLock); const ScopedLock sl (CoreMidiHelpers::callbackLock);
static_cast<CoreMidiHelpers::MidiPortAndCallback*> (internal)->active = true;
internal->active = true;
} }
void MidiInput::stop() void MidiInput::stop()
{ {
const ScopedLock sl (CoreMidiHelpers::callbackLock); 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() Array<MidiDeviceInfo> MidiOutput::getAvailableDevices()
{ {
return CoreMidiHelpers::findDevices (false); 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))) if (CHECK_ERROR (MIDIOutputPortCreate (client, cfName.cfString, &port)))
{ {
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (endpointInfo.name, endpointInfo.identifier)); 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; return midiOutput;
} }
@@ -622,7 +633,7 @@ std::unique_ptr<MidiOutput> MidiOutput::createNewDevice (const String& deviceNam
if (CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier))) if (CHECK_ERROR (MIDIObjectSetIntegerProperty (endpoint, kMIDIPropertyUniqueID, (SInt32) deviceIdentifier)))
{ {
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (deviceName, String (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; return midiOutput;
} }
@@ -655,8 +666,6 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (int index)
MidiOutput::~MidiOutput() MidiOutput::~MidiOutput()
{ {
stopBackgroundThread(); stopBackgroundThread();
delete static_cast<CoreMidiHelpers::MidiPortAndEndpoint*> (internal);
} }
void MidiOutput::sendMessageNow (const MidiMessage& message) void MidiOutput::sendMessageNow (const MidiMessage& message)
@@ -715,7 +724,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message)
return; return;
} }
static_cast<CoreMidiHelpers::MidiPortAndEndpoint*> (internal)->send (packetToSend);
internal->send (packetToSend);
} }
#undef CHECK_ERROR #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); inBuffers[n] = ioBufferSpace + (currentBlockSizeSamples * n);
ASIOChannelInfo channelInfo = { 0 };
ASIOChannelInfo channelInfo = {};
channelInfo.channel = i; channelInfo.channel = i;
channelInfo.isInput = 1; channelInfo.isInput = 1;
asioObject->getChannelInfo (&channelInfo); asioObject->getChannelInfo (&channelInfo);
@@ -526,7 +526,7 @@ public:
{ {
outBuffers[n] = ioBufferSpace + (currentBlockSizeSamples * (numActiveInputChans + n)); outBuffers[n] = ioBufferSpace + (currentBlockSizeSamples * (numActiveInputChans + n));
ASIOChannelInfo channelInfo = { 0 };
ASIOChannelInfo channelInfo = {};
channelInfo.channel = i; channelInfo.channel = i;
channelInfo.isInput = 0; channelInfo.isInput = 0;
asioObject->getChannelInfo (&channelInfo); asioObject->getChannelInfo (&channelInfo);
@@ -673,10 +673,10 @@ public:
lastCallback->audioDeviceStopped(); 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"); JUCE_ASIO_LOG ("showing control panel");
@@ -767,7 +767,7 @@ private:
bool deviceIsOpen = false, isStarted = false, buffersCreated = false; bool deviceIsOpen = false, isStarted = false, buffersCreated = false;
std::atomic<bool> calledback { false }; std::atomic<bool> calledback { false };
bool littleEndian = false, postOutput = true, needToReset = false;
bool postOutput = true, needToReset = false;
bool insideControlPanelModalLoop = false; bool insideControlPanelModalLoop = false;
bool shouldUsePreferredSize = false; bool shouldUsePreferredSize = false;
int xruns = 0; int xruns = 0;
@@ -785,7 +785,7 @@ private:
String getChannelName (int index, bool isInput) const String getChannelName (int index, bool isInput) const
{ {
ASIOChannelInfo channelInfo = { 0 };
ASIOChannelInfo channelInfo = {};
channelInfo.channel = index; channelInfo.channel = index;
channelInfo.isInput = isInput ? 1 : 0; channelInfo.isInput = isInput ? 1 : 0;
asioObject->getChannelInfo (&channelInfo); asioObject->getChannelInfo (&channelInfo);
@@ -1065,7 +1065,7 @@ private:
for (int i = 0; i < totalNumOutputChans; ++i) for (int i = 0; i < totalNumOutputChans; ++i)
{ {
ASIOChannelInfo channelInfo = { 0 };
ASIOChannelInfo channelInfo = {};
channelInfo.channel = i; channelInfo.channel = i;
channelInfo.isInput = 0; channelInfo.isInput = 0;
asioObject->getChannelInfo (&channelInfo); asioObject->getChannelInfo (&channelInfo);
@@ -1640,9 +1640,4 @@ void sendASIODeviceChangeToListeners (ASIOAudioIODeviceType* type)
type->sendDeviceChangeToListeners(); type->sendDeviceChangeToListeners();
} }
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO()
{
return new ASIOAudioIODeviceType();
}
} // namespace juce } // 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) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType)
}; };
//==============================================================================
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound()
{
return new DSoundAudioIODeviceType();
}
} // namespace juce } // namespace juce

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

@@ -29,37 +29,39 @@
namespace juce 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 Array<MidiDeviceInfo> getAvailableDevices (bool) = 0;
virtual MidiDeviceInfo getDefaultDevice (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) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiServiceType)
}; };
@@ -82,12 +84,12 @@ struct Win32MidiService : public MidiServiceType,
: Win32OutputWrapper::getDefaultDevice(); : 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); 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); return new Win32OutputWrapper (*this, deviceIdentifier);
} }
@@ -384,7 +386,7 @@ private:
} }
}; };
struct Win32InputWrapper : public InputWrapper,
struct Win32InputWrapper : public MidiInput::Pimpl,
public Win32MidiDeviceQuery<Win32InputWrapper> public Win32MidiDeviceQuery<Win32InputWrapper>
{ {
Win32InputWrapper (Win32MidiService& parentService, MidiInput& midiInput, const String& deviceIdentifier, MidiInputCallback& c) 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> public Win32MidiDeviceQuery<Win32OutputWrapper>
{ {
Win32OutputWrapper (Win32MidiService& p, const String& deviceIdentifier) Win32OutputWrapper (Win32MidiService& p, const String& deviceIdentifier)
@@ -763,12 +765,12 @@ public:
: outputDeviceWatcher->getDefaultDevice(); : 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); 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); 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> private WinRTIOWrapper<IMidiInPortStatics, IMidiInPort>
{ {
@@ -1708,7 +1710,7 @@ private:
}; };
//============================================================================== //==============================================================================
struct WinRTOutputWrapper final : public OutputWrapper,
struct WinRTOutputWrapper final : public MidiOutput::Pimpl,
private WinRTIOWrapper <IMidiOutPortStatics, IMidiOutPort> private WinRTIOWrapper <IMidiOutPortStatics, IMidiOutPort>
{ {
WinRTOutputWrapper (WinRTMidiService& service, const String& deviceIdentifier) WinRTOutputWrapper (WinRTMidiService& service, const String& deviceIdentifier)
@@ -1865,7 +1867,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier
return {}; return {};
std::unique_ptr<MidiInput> in (new MidiInput ({}, deviceIdentifier)); std::unique_ptr<MidiInput> in (new MidiInput ({}, deviceIdentifier));
std::unique_ptr<MidiServiceType::InputWrapper> wrapper;
std::unique_ptr<Pimpl> wrapper;
try try
{ {
@@ -1877,7 +1879,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier
} }
in->setName (wrapper->getDeviceName()); in->setName (wrapper->getDeviceName());
in->internal = wrapper.release();
in->internal = std::move (wrapper);
return in; 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() Array<MidiDeviceInfo> MidiOutput::getAvailableDevices()
@@ -1931,7 +1930,7 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifi
if (deviceIdentifier.isEmpty()) if (deviceIdentifier.isEmpty())
return {}; return {};
std::unique_ptr<MidiServiceType::OutputWrapper> wrapper;
std::unique_ptr<Pimpl> wrapper;
try try
{ {
@@ -1945,7 +1944,7 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifi
std::unique_ptr<MidiOutput> out; std::unique_ptr<MidiOutput> out;
out.reset (new MidiOutput (wrapper->getDeviceName(), deviceIdentifier)); out.reset (new MidiOutput (wrapper->getDeviceName(), deviceIdentifier));
out->internal = wrapper.release();
out->internal = std::move (wrapper);
return out; return out;
} }
@@ -1973,12 +1972,11 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (int index)
MidiOutput::~MidiOutput() MidiOutput::~MidiOutput()
{ {
stopBackgroundThread(); stopBackgroundThread();
delete static_cast<MidiServiceType::OutputWrapper*> (internal);
} }
void MidiOutput::sendMessageNow (const MidiMessage& message) void MidiOutput::sendMessageNow (const MidiMessage& message)
{ {
static_cast<MidiServiceType::OutputWrapper*> (internal)->sendMessageNow (message);
internal->sendMessageNow (message);
} }
} // namespace juce } // 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 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_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2")
{ {
JUCE_COMCALL Initialize (AUDCLNT_SHAREMODE, DWORD, REFERENCE_TIME, REFERENCE_TIME, const WAVEFORMATEX*, LPCGUID) = 0; 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_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_IUNKNOWNCLASS (IAudioCaptureClient, "C8ADBD64-E71E-48a0-A4DE-185C395CD317")
{ {
JUCE_COMCALL GetBuffer (BYTE**, UINT32*, DWORD*, UINT64*, UINT64*) = 0; JUCE_COMCALL GetBuffer (BYTE**, UINT32*, DWORD*, UINT64*, UINT64*) = 0;
@@ -322,87 +359,76 @@ String getDeviceID (IMMDevice* device)
return s; return s;
} }
EDataFlow getDataFlow (const ComSmartPtr<IMMDevice>& device)
static EDataFlow getDataFlow (const ComSmartPtr<IMMDevice>& device)
{ {
EDataFlow flow = eRender; 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; 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); 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); 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) memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE)
: sizeof (WAVEFORMATEX)); : 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 class WASAPIDeviceBase
{ {
public: 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); clientEvent = CreateEvent (nullptr, false, false, nullptr);
ComSmartPtr<IAudioClient> tempClient (createClient()); ComSmartPtr<IAudioClient> tempClient (createClient());
if (tempClient == nullptr) if (tempClient == nullptr)
return; 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; return;
WAVEFORMATEXTENSIBLE format;
copyWavFormat (format, mixFormat);
CoTaskMemFree (mixFormat);
actualNumChannels = numChannels = format.Format.nChannels; actualNumChannels = numChannels = format.Format.nChannels;
defaultSampleRate = format.Format.nSamplesPerSec; defaultSampleRate = format.Format.nSamplesPerSec;
minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate);
defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate);
mixFormatChannelMask = format.dwChannelMask;
rates.addUsingDefaultSort (defaultSampleRate); 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() virtual ~WASAPIDeviceBase()
@@ -487,11 +513,14 @@ public:
//============================================================================== //==============================================================================
ComSmartPtr<IMMDevice> device; ComSmartPtr<IMMDevice> device;
ComSmartPtr<IAudioClient> client; ComSmartPtr<IAudioClient> client;
WASAPIDeviceMode deviceMode;
double sampleRate = 0, defaultSampleRate = 0; double sampleRate = 0, defaultSampleRate = 0;
int numChannels = 0, actualNumChannels = 0; int numChannels = 0, actualNumChannels = 0;
int minBufferSize = 0, defaultBufferSize = 0, latencySamples = 0; int minBufferSize = 0, defaultBufferSize = 0, latencySamples = 0;
int lowLatencyBufferSizeMultiple = 0, lowLatencyMaxBufferSize = 0;
DWORD mixFormatChannelMask = 0; DWORD mixFormatChannelMask = 0;
const bool useExclusiveMode;
Array<double> rates; Array<double> rates;
HANDLE clientEvent = {}; HANDLE clientEvent = {};
BigInteger channels; BigInteger channels;
@@ -582,6 +611,84 @@ private:
return newClient; 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 struct AudioSampleFormat
{ {
bool useFloat; bool useFloat;
@@ -613,22 +720,35 @@ private:
format.SubFormat = sampleFormat.useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; format.SubFormat = sampleFormat.useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM;
format.dwChannelMask = newMixFormatChannelMask; 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, (WAVEFORMATEX*) &format,
useExclusiveMode ? nullptr : (WAVEFORMATEX**) &nearestFormat);
isExclusiveMode (deviceMode) ? nullptr
: &nearestFormat);
logFailure (hr); 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; hr = S_OK;
} }
CoTaskMemFree (nearestFormat); CoTaskMemFree (nearestFormat);
return check (hr);
return hr == S_OK;
} }
bool findSupportedFormat (IAudioClient* clientToUse, double newSampleRate, bool findSupportedFormat (IAudioClient* clientToUse, double newSampleRate,
@@ -652,50 +772,88 @@ private:
return false; 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 class WASAPIInputDevice : public WASAPIDeviceBase
{ {
public: 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 class WASAPIOutputDevice : public WASAPIDeviceBase
{ {
public: 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) if (numChannels <= 0)
return 0; return 0;
if (! useExclusiveMode)
if (! isExclusiveMode (deviceMode))
{ {
UINT32 padding = 0; UINT32 padding = 0;
@@ -953,7 +1111,7 @@ public:
while (bufferSize > 0) while (bufferSize > 0)
{ {
// This is needed in order not to drop any input data if the output device endpoint buffer was full // 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) && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
inputDevice->handleDeviceBuffer(); inputDevice->handleDeviceBuffer();
@@ -968,7 +1126,7 @@ public:
break; break;
} }
if (useExclusiveMode && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT)
if (isExclusiveMode (deviceMode) && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT)
break; break;
uint8* outputData = nullptr; uint8* outputData = nullptr;
@@ -1002,12 +1160,12 @@ public:
const String& typeName, const String& typeName,
const String& outputDeviceID, const String& outputDeviceID,
const String& inputDeviceID, const String& inputDeviceID,
bool exclusiveMode)
WASAPIDeviceMode mode)
: AudioIODevice (deviceName, typeName), : AudioIODevice (deviceName, typeName),
Thread ("JUCE WASAPI"), Thread ("JUCE WASAPI"),
outputDeviceId (outputDeviceID), outputDeviceId (outputDeviceID),
inputDeviceId (inputDeviceID), inputDeviceId (inputDeviceID),
useExclusiveMode (exclusiveMode)
deviceMode (mode)
{ {
} }
@@ -1026,21 +1184,48 @@ public:
{ {
jassert (inputDevice != nullptr || outputDevice != nullptr); jassert (inputDevice != nullptr || outputDevice != nullptr);
sampleRates.clear();
if (inputDevice != nullptr && outputDevice != nullptr) if (inputDevice != nullptr && outputDevice != nullptr)
{ {
defaultSampleRate = jmin (inputDevice->defaultSampleRate, outputDevice->defaultSampleRate); defaultSampleRate = jmin (inputDevice->defaultSampleRate, outputDevice->defaultSampleRate);
minBufferSize = jmin (inputDevice->minBufferSize, outputDevice->minBufferSize);
minBufferSize = jmax (inputDevice->minBufferSize, outputDevice->minBufferSize);
defaultBufferSize = jmax (inputDevice->defaultBufferSize, outputDevice->defaultBufferSize); 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 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; defaultSampleRate = d->defaultSampleRate;
minBufferSize = d->minBufferSize; minBufferSize = d->minBufferSize;
defaultBufferSize = d->defaultBufferSize; defaultBufferSize = d->defaultBufferSize;
if (isLowLatencyMode (deviceMode))
{
lowLatencyMaxBufferSize = d->lowLatencyMaxBufferSize;
lowLatencyBufferSizeMultiple = d->lowLatencyBufferSizeMultiple;
}
sampleRates = d->rates; sampleRates = d->rates;
} }
@@ -1050,13 +1235,28 @@ public:
if (minBufferSize != defaultBufferSize) if (minBufferSize != defaultBufferSize)
bufferSizes.addUsingDefaultSort (minBufferSize); 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; return true;
@@ -1131,7 +1331,7 @@ public:
return lastError; return lastError;
} }
if (useExclusiveMode)
if (isExclusiveMode (deviceMode))
{ {
// This is to make sure that the callback uses actualBufferSize in case of exclusive mode // This is to make sure that the callback uses actualBufferSize in case of exclusive mode
if (inputDevice != nullptr && outputDevice != nullptr && inputDevice->actualBufferSize != outputDevice->actualBufferSize) if (inputDevice != nullptr && outputDevice != nullptr && inputDevice->actualBufferSize != outputDevice->actualBufferSize)
@@ -1297,7 +1497,7 @@ public:
} }
else else
{ {
if (useExclusiveMode && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
if (isExclusiveMode (deviceMode) && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
inputDevice->handleDeviceBuffer(); inputDevice->handleDeviceBuffer();
} }
@@ -1347,9 +1547,10 @@ private:
// Device stats... // Device stats...
std::unique_ptr<WASAPIInputDevice> inputDevice; std::unique_ptr<WASAPIInputDevice> inputDevice;
std::unique_ptr<WASAPIOutputDevice> outputDevice; std::unique_ptr<WASAPIOutputDevice> outputDevice;
const bool useExclusiveMode;
WASAPIDeviceMode deviceMode;
double defaultSampleRate = 0; double defaultSampleRate = 0;
int minBufferSize = 0, defaultBufferSize = 0; int minBufferSize = 0, defaultBufferSize = 0;
int lowLatencyMaxBufferSize = 0, lowLatencyBufferSizeMultiple = 0;
int latencyIn = 0, latencyOut = 0; int latencyIn = 0, latencyOut = 0;
Array<double> sampleRates; Array<double> sampleRates;
Array<int> bufferSizes; Array<int> bufferSizes;
@@ -1399,9 +1600,9 @@ private:
auto flow = getDataFlow (device); auto flow = getDataFlow (device);
if (deviceId == inputDeviceId && flow == eCapture) if (deviceId == inputDeviceId && flow == eCapture)
inputDevice.reset (new WASAPIInputDevice (device, useExclusiveMode));
inputDevice.reset (new WASAPIInputDevice (device, deviceMode));
else if (deviceId == outputDeviceId && flow == eRender) 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())) return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk()))
@@ -1458,10 +1659,10 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType,
private DeviceChangeDetector private DeviceChangeDetector
{ {
public: public:
WASAPIAudioIODeviceType (bool exclusive)
: AudioIODeviceType (exclusive ? "Windows Audio (Exclusive Mode)" : "Windows Audio"),
WASAPIAudioIODeviceType (WASAPIDeviceMode mode)
: AudioIODeviceType (getDeviceTypename (mode)),
DeviceChangeDetector (L"Windows Audio"), DeviceChangeDetector (L"Windows Audio"),
exclusiveMode (exclusive)
deviceMode (mode)
{ {
} }
@@ -1529,7 +1730,7 @@ public:
getTypeName(), getTypeName(),
outputDeviceIds [outputIndex], outputDeviceIds [outputIndex],
inputDeviceIds [inputIndex], inputDeviceIds [inputIndex],
exclusiveMode));
deviceMode));
if (! device->initialise()) if (! device->initialise())
device = nullptr; device = nullptr;
@@ -1543,7 +1744,7 @@ public:
StringArray inputDeviceNames, inputDeviceIds; StringArray inputDeviceNames, inputDeviceIds;
private: private:
const bool exclusiveMode;
WASAPIDeviceMode deviceMode;
bool hasScanned = false; bool hasScanned = false;
ComSmartPtr<IMMDeviceEnumerator> enumerator; ComSmartPtr<IMMDeviceEnumerator> enumerator;
@@ -1698,6 +1899,17 @@ private:
callDeviceChangeListeners(); 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_WEAK_REFERENCEABLE (WASAPIAudioIODeviceType)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (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 #define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1
float JUCE_CALLTYPE SystemAudioVolume::getGain() { return WasapiClasses::MMDeviceMasterVolume().getGain(); } 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 #ifndef JUCE_SUPPORTS_AUv3
#if __OBJC2__ \ #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 #define JUCE_SUPPORTS_AUv3 1
#else #else
#define JUCE_SUPPORTS_AUv3 0 #define JUCE_SUPPORTS_AUv3 0
@@ -79,7 +78,7 @@ namespace AudioUnitFormatHelpers
static ThreadLocalValue<int> insideCallback; static ThreadLocalValue<int> insideCallback;
#endif #endif
String osTypeToString (OSType type) noexcept
static String osTypeToString (OSType type) noexcept
{ {
const juce_wchar s[4] = { (juce_wchar) ((type >> 24) & 0xff), const juce_wchar s[4] = { (juce_wchar) ((type >> 24) & 0xff),
(juce_wchar) ((type >> 16) & 0xff), (juce_wchar) ((type >> 16) & 0xff),
@@ -88,7 +87,7 @@ namespace AudioUnitFormatHelpers
return String (s, 4); return String (s, 4);
} }
OSType stringToOSType (String s)
static OSType stringToOSType (String s)
{ {
if (s.trim().length() >= 4) // (to avoid trimming leading spaces) if (s.trim().length() >= 4) // (to avoid trimming leading spaces)
s = s.trim(); s = s.trim();
@@ -103,7 +102,7 @@ namespace AudioUnitFormatHelpers
static const char* auIdentifierPrefix = "AudioUnit:"; static const char* auIdentifierPrefix = "AudioUnit:";
String createPluginIdentifier (const AudioComponentDescription& desc)
static String createPluginIdentifier (const AudioComponentDescription& desc)
{ {
String s (auIdentifierPrefix); String s (auIdentifierPrefix);
@@ -128,7 +127,7 @@ namespace AudioUnitFormatHelpers
return s; return s;
} }
void getNameAndManufacturer (AudioComponent comp, String& name, String& manufacturer)
static void getNameAndManufacturer (AudioComponent comp, String& name, String& manufacturer)
{ {
CFStringRef cfName; CFStringRef cfName;
if (AudioComponentCopyName (comp, &cfName) == noErr) if (AudioComponentCopyName (comp, &cfName) == noErr)
@@ -147,8 +146,8 @@ namespace AudioUnitFormatHelpers
name = "<Unknown>"; 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)) if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix))
{ {
@@ -193,8 +192,8 @@ namespace AudioUnitFormatHelpers
return false; 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); zerostruct (desc);
@@ -296,7 +295,7 @@ namespace AudioUnitFormatHelpers
#endif #endif
} }
const char* getCategory (OSType type) noexcept
static const char* getCategory (OSType type) noexcept
{ {
switch (type) 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 class MidiEventList : public Steinberg::Vst::IEventList
{ {
public: public:
MidiEventList() {}
virtual ~MidiEventList() {}
MidiEventList() = default;
virtual ~MidiEventList() = default;
JUCE_DECLARE_VST3_COM_REF_METHODS JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_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, 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 enum { maxNumEvents = 2048 }; // Steinberg's Host Checker states that no more than 2048 events are allowed at once
int numEvents = 0; int numEvents = 0;
@@ -491,7 +525,7 @@ public:
} }
} }
auto maybeEvent = createVstEvent (msg, metadata.data);
auto maybeEvent = createVstEvent (msg, metadata.data, kind);
if (! maybeEvent.isValid) if (! maybeEvent.isValid)
continue; continue;
@@ -503,7 +537,6 @@ public:
} }
} }
private:
Array<Steinberg::Vst::Event, CriticalSection> events; Array<Steinberg::Vst::Event, CriticalSection> events;
Atomic<int> refCount; Atomic<int> refCount;
@@ -555,10 +588,21 @@ private:
{ {
Steinberg::Vst::Event e{}; Steinberg::Vst::Event e{};
e.type = Steinberg::Vst::Event::kLegacyMIDICCOutEvent; 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.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; return e;
} }
@@ -610,14 +654,15 @@ private:
struct BasicOptional final struct BasicOptional final
{ {
BasicOptional() noexcept = default; BasicOptional() noexcept = default;
BasicOptional (const Item& i) noexcept : item ( i ), isValid ( true ) {}
BasicOptional (const Item& i) noexcept : item { i }, isValid { true } {}
Item item; Item item;
bool isValid{}; bool isValid{};
}; };
static BasicOptional<Steinberg::Vst::Event> createVstEvent (const MidiMessage& msg, static BasicOptional<Steinberg::Vst::Event> createVstEvent (const MidiMessage& msg,
const uint8* midiEventData) noexcept
const uint8* midiEventData,
EventConversionKind kind) noexcept
{ {
if (msg.isNoteOn()) if (msg.isNoteOn())
return createNoteOnEvent (msg); return createNoteOnEvent (msg);
@@ -643,11 +688,20 @@ private:
if (msg.isQuarterFrame()) if (msg.isQuarterFrame())
return createCtrlQuarterFrameEvent (msg); 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()) if (msg.isAftertouch())
return createCtrlPolyPressureEvent (msg);
{
switch (kind)
{
case EventConversionKind::hostToPlugin:
return createPolyPressureEvent (msg);
case EventConversionKind::pluginToHost:
return createCtrlPolyPressureEvent (msg);
}
jassertfalse;
return {};
}
return {}; return {};
} }
@@ -738,13 +792,26 @@ private:
static bool toVst3ControlEvent (const MidiMessage& msg, Vst3MidiControlEvent& result) 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) 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", "-Wformat",
"-Wpedantic", "-Wpedantic",
"-Wextra", "-Wextra",
"-Wclass-memaccess")
"-Wclass-memaccess",
"-Wmissing-prototypes",
"-Wtype-limits")
#undef DEVELOPMENT #undef DEVELOPMENT
#define DEVELOPMENT 0 // This avoids a Clang warning in Steinberg code about unused values #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 struct DLLHandle
{ {
DLLHandle (const String& modulePath)
DLLHandle (const File& fileToOpen)
: dllFile (fileToOpen)
{ {
if (modulePath.trim().isNotEmpty())
open (modulePath);
open();
} }
~DLLHandle() ~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) 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(); exitFn();
CFBundleUnloadExecutable (bundleRef);
#if JUCE_WINDOWS || JUCE_LINUX
library.close();
#elif JUCE_MAC
CFRelease (bundleRef); CFRelease (bundleRef);
bundleRef = nullptr; 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() IPluginFactory* JUCE_CALLTYPE getPluginFactory()
{ {
if (factory == nullptr) if (factory == nullptr)
if (auto proc = (GetFactoryProc) getFunction ("GetPluginFactory"))
if (auto* proc = (GetFactoryProc) getFunction (factoryFnName))
factory = proc(); factory = proc();
// The plugin NEEDS to provide a factory to be able to be called a VST3! // The plugin NEEDS to provide a factory to be able to be called a VST3!
@@ -894,38 +872,56 @@ struct DLLHandle
if (bundleRef == nullptr) if (bundleRef == nullptr)
return 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 #endif
} }
File getFile() const noexcept { return dllFile; }
private: private:
File dllFile;
IPluginFactory* factory = nullptr; IPluginFactory* factory = nullptr;
void releaseFactory()
{
if (factory != nullptr)
factory->release();
}
static constexpr const char* factoryFnName = "GetPluginFactory";
#if JUCE_WINDOWS #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; 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()) if (proc())
#else
if (proc (library.getNativeHandle()))
#endif
return true; return true;
} }
else else
{ {
// this is required for some plug-ins which don't export the dll entry point function
return true; return true;
} }
@@ -937,12 +933,14 @@ private:
#elif JUCE_MAC #elif JUCE_MAC
CFBundleRef bundleRef; 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); bundleRef = CFBundleCreate (kCFAllocatorDefault, url);
CFRelease (url); CFRelease (url);
@@ -953,17 +951,11 @@ private:
if (CFBundleLoadExecutableAndReturnError (bundleRef, &error)) if (CFBundleLoadExecutableAndReturnError (bundleRef, &error))
{ {
using BundleEntryProc = bool (*)(CFBundleRef);
if (auto proc = (BundleEntryProc) getFunction ("bundleEntry"))
if (auto* proc = (EntryProc) getFunction (entryFnName))
{ {
if (proc (bundleRef)) if (proc (bundleRef))
return true; return true;
} }
else
{
return true;
}
} }
if (error != nullptr) if (error != nullptr)
@@ -984,127 +976,122 @@ private:
return false; 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 #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 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() ~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>; 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()) for (auto* module : getActiveModules())
{
// VST3s are basically shells, you must therefore check their name along with their file: // VST3s are basically shells, you must therefore check their name along with their file:
if (module->file == file && module->name == description.name) if (module->file == file && module->name == description.name)
return module; 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: private:
std::unique_ptr<DLLHandle> dllHandle;
//============================================================================== //==============================================================================
static Array<VST3ModuleHandle*>& getActiveModules() 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) if (pluginFactory != nullptr)
{ {
@@ -1132,7 +1118,7 @@ private:
continue; continue;
if (toString (info.name).trim() == description.name if (toString (info.name).trim() == description.name
&& getHashForTUID (info.cid) == description.uid)
&& getHashForTUID (info.cid) == description.uid)
{ {
name = description.name; name = description.name;
return true; return true;
@@ -1143,6 +1129,11 @@ private:
return false; return false;
} }
File file;
String name;
bool isOpen = false;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3ModuleHandle) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3ModuleHandle)
}; };
@@ -1618,7 +1609,7 @@ struct VST3ComponentHolder
PFactoryInfo factoryInfo; PFactoryInfo factoryInfo;
factory->getFactoryInfo (&factoryInfo); factory->getFactoryInfo (&factoryInfo);
auto classIdx = getClassIndex (module->name);
auto classIdx = getClassIndex (module->getName());
if (classIdx >= 0) if (classIdx >= 0)
{ {
@@ -1667,8 +1658,8 @@ struct VST3ComponentHolder
if (component->getBusInfo (Vst::kAudio, Vst::kOutput, i, bus) == kResultOk) if (component->getBusInfo (Vst::kAudio, Vst::kOutput, i, bus) == kResultOk)
totalNumOutputChannels += ((bus.flags & Vst::BusInfo::kDefaultActive) != 0 ? bus.channelCount : 0); 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(), info, info2.get(), infoW.get(),
totalNumInputChannels, totalNumInputChannels,
totalNumOutputChannels); totalNumOutputChannels);
@@ -1695,7 +1686,7 @@ struct VST3ComponentHolder
factory = ComSmartPtr<IPluginFactory> (module->getPluginFactory()); factory = ComSmartPtr<IPluginFactory> (module->getPluginFactory());
int classIdx; int classIdx;
if ((classIdx = getClassIndex (module->name)) < 0)
if ((classIdx = getClassIndex (module->getName())) < 0)
return false; return false;
PClassInfo info; PClassInfo info;
@@ -1991,7 +1982,7 @@ public:
const String getName() const override const String getName() const override
{ {
auto& module = holder->module; 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 void repopulateArrangements (Array<Vst::SpeakerArrangement>& inputArrangements, Array<Vst::SpeakerArrangement>& outputArrangements) const
@@ -2979,9 +2970,10 @@ private:
midiOutputs->clear(); midiOutputs->clear();
if (acceptsMidi()) if (acceptsMidi())
MidiEventList::toEventList (*midiInputs, midiBuffer,
destination.inputParameterChanges,
midiMapping);
MidiEventList::hostToPluginEventList (*midiInputs,
midiBuffer,
destination.inputParameterChanges,
midiMapping);
destination.inputEvents = midiInputs; destination.inputEvents = midiInputs;
destination.outputEvents = midiOutputs; destination.outputEvents = midiOutputs;
@@ -3315,7 +3307,29 @@ bool VST3PluginFormat::setStateFromVSTPresetFile (AudioPluginInstance* api, cons
void VST3PluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& results, const String& fileOrIdentifier) void VST3PluginFormat::findAllTypesForFile (OwnedArray<PluginDescription>& results, const String& fileOrIdentifier)
{ {
if (fileMightContainThisPluginType (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, 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 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 /** 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 #define JUCE_VSTINTERFACE_H_INCLUDED
using namespace juce; using namespace juce;
@@ -505,7 +506,7 @@ enum PresonusExtensionConstants
@tags{Audio} @tags{Audio}
*/ */
struct vst2FxBank
struct fxBank
{ {
int32 magic1; int32 magic1;
int32 size; int32 size;
@@ -527,3 +528,5 @@ struct vst2FxBank
#else #else
#pragma pack(pop) #pragma pack(pop)
#endif #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 #elif JUCE_LINUX || JUCE_IOS || JUCE_ANDROID
timeval micro; timeval micro;
gettimeofday (&micro, nullptr); gettimeofday (&micro, nullptr);
return micro.tv_usec * 1000.0;
return (double) micro.tv_usec * 1000.0;
#elif JUCE_MAC #elif JUCE_MAC
UnsignedWide micro; UnsignedWide micro;
Microseconds (&micro); Microseconds (&micro);
@@ -441,8 +441,8 @@ private:
} }
else 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.inclusiveLow = true;
entry->range.inclusiveHigh = (curEntry == numEntries - 1); entry->range.inclusiveHigh = (curEntry == numEntries - 1);
@@ -2872,8 +2872,8 @@ public:
{ {
X11Symbols::getInstance()->xMoveResizeWindow (display, pluginWindow, X11Symbols::getInstance()->xMoveResizeWindow (display, pluginWindow,
pos.getX(), pos.getY(), 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()->xMapRaised (display, pluginWindow);
X11Symbols::getInstance()->xFlush (display); X11Symbols::getInstance()->xFlush (display);
@@ -2939,8 +2939,8 @@ public:
if (pluginRespondsToDPIChanges) if (pluginRespondsToDPIChanges)
dispatch (Vst2::plugInOpcodeManufacturerSpecific, 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); nullptr, nativeScaleFactor);
} }
#endif #endif
@@ -3164,8 +3164,8 @@ private:
X11Symbols::getInstance()->xMapRaised (display, pluginWindow); X11Symbols::getInstance()->xMapRaised (display, pluginWindow);
#endif #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 // double-check it's not too tiny
w = jmax (w, 32); w = jmax (w, 32);
@@ -3631,7 +3631,6 @@ FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch()
FileSearchPath paths; FileSearchPath paths;
paths.add (WindowsRegistry::getValue ("HKEY_LOCAL_MACHINE\\Software\\VST\\VSTPluginsPath")); paths.add (WindowsRegistry::getValue ("HKEY_LOCAL_MACHINE\\Software\\VST\\VSTPluginsPath"));
paths.addIfNotAlreadyThere (programFiles + "\\Steinberg\\VstPlugins"); paths.addIfNotAlreadyThere (programFiles + "\\Steinberg\\VstPlugins");
paths.removeNonExistentPaths();
paths.addIfNotAlreadyThere (programFiles + "\\VstPlugins"); paths.addIfNotAlreadyThere (programFiles + "\\VstPlugins");
paths.removeRedundantPaths(); paths.removeRedundantPaths();
return paths; return paths;


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

@@ -35,7 +35,7 @@
ID: juce_audio_processors ID: juce_audio_processors
vendor: juce vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE audio processor classes name: JUCE audio processor classes
description: Classes for loading and playing VST, AU, LADSPA, or internally-generated audio processors. description: Classes for loading and playing VST, AU, LADSPA, or internally-generated audio processors.
website: http://www.juce.com/juce 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); 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 } // namespace juce

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

@@ -191,6 +191,7 @@ ComboBoxParameterAttachment::ComboBoxParameterAttachment (RangedAudioParameter&
ComboBox& c, ComboBox& c,
UndoManager* um) UndoManager* um)
: comboBox (c), : comboBox (c),
storedParameter (param),
attachment (param, [this] (float f) { setValue (f); }, um) attachment (param, [this] (float f) { setValue (f); }, um)
{ {
sendInitialUpdate(); sendInitialUpdate();
@@ -209,7 +210,8 @@ void ComboBoxParameterAttachment::sendInitialUpdate()
void ComboBoxParameterAttachment::setValue (float newValue) 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()) if (index == comboBox.getSelectedItemIndex())
return; return;
@@ -223,7 +225,12 @@ void ComboBoxParameterAttachment::comboBoxChanged (ComboBox*)
if (ignoreCallbacks) if (ignoreCallbacks)
return; 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; void comboBoxChanged (ComboBox*) override;
ComboBox& comboBox; ComboBox& comboBox;
RangedAudioParameter& storedParameter;
ParameterAttachment attachment; ParameterAttachment attachment;
bool ignoreCallbacks = false; 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(); 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; return tct == ntct;
} }


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

@@ -257,8 +257,8 @@ public:
} }
private: 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__ #if __LP64__
using OSType = unsigned int; using OSType = unsigned int;
#else #else


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

@@ -23,6 +23,14 @@
namespace juce 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_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
@@ -37,6 +45,7 @@ RangedDirectoryIterator::RangedDirectoryIterator (const File& directory,
wildCard, wildCard,
whatToLookFor)) whatToLookFor))
{ {
entry.iterator = iterator;
increment(); 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. */ /** True if the item is read-only, false otherwise. */
bool isReadOnly() const { return readOnly; } 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: private:
std::weak_ptr<DirectoryIterator> iterator;
File file; File file;
Time modTime; Time modTime;
Time creationTime; Time creationTime;


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

@@ -32,7 +32,7 @@
ID: juce_core ID: juce_core
vendor: juce vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE core classes 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. 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 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> #include <libkern/OSByteOrder.h>
#endif #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> template <typename Type>
inline void deleteAndZero (Type& pointer) { delete pointer; pointer = nullptr; } 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. /** 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. */ alignmentBytes must be a power of two. */
template <typename Type, typename IntegerType> template <typename Type, typename IntegerType>
@@ -83,6 +76,53 @@ inline void writeUnaligned (void* dstPtr, Type value) noexcept
memcpy (dstPtr, &value, sizeof (Type)); 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 #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 bool Time::setSystemTimeToThisTime() const
{ {
timeval t; 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; 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; StringArray params;
params.addTokens (parameters, true); params.addTokens (parameters, true);
NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease];
NSMutableArray* paramArray = [[NSMutableArray new] autorelease]; NSMutableArray* paramArray = [[NSMutableArray new] autorelease];
for (int i = 0; i < params.size(); ++i) for (int i = 0; i < params.size(); ++i)
[paramArray addObject: juceStringToNS (params[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 [dict setObject: paramArray
forKey: nsStringLiteral ("NSWorkspaceLaunchConfigurationArguments")]; forKey: nsStringLiteral ("NSWorkspaceLaunchConfigurationArguments")];
@@ -438,6 +448,7 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&
options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance
configuration: dict configuration: dict
error: nil]; error: nil];
#endif
} }
if (file.exists()) 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; StringArray parts;
parts.addTokens (getOSXVersion(), ".", StringRef()); 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 #endif
} }
@@ -199,10 +205,8 @@ bool SystemStats::isOperatingSystem64Bit()
{ {
#if JUCE_IOS #if JUCE_IOS
return false; return false;
#elif JUCE_64BIT
return true;
#else #else
return getOperatingSystemType() >= MacOSX_10_6;
return true;
#endif #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())); static_cast<CGFloat> (r.getHeight()));
} }
#endif #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 #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 struct NSObjectDeleter
{ {
@@ -236,7 +240,10 @@ struct ObjCClass
~ObjCClass() ~ObjCClass()
{ {
objc_disposeClassPair (cls);
auto kvoSubclassName = String ("NSKVONotifying_") + class_getName (cls);
if (objc_getClass (kvoSubclassName.toUTF8()) == nullptr)
objc_disposeClassPair (cls);
} }
void registerClass() void registerClass()
@@ -287,13 +294,11 @@ struct ObjCClass
jassert (b); ignoreUnused (b); 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> template <typename Type>
static Type getIvar (id self, const char* name) static Type getIvar (id self, const char* name)
@@ -330,18 +335,14 @@ struct ObjCLifetimeManagedClass : public ObjCClass<NSObject>
addMethod (@selector (dealloc), dealloc, "v@:"); addMethod (@selector (dealloc), dealloc, "v@:");
registerClass(); registerClass();
} }
static id initWithJuceObject (id _self, SEL, JuceClass* obj) 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); object_setInstanceVariable (self, "cppObject", obj);
return self; return self;
} }
@@ -353,11 +354,9 @@ struct ObjCLifetimeManagedClass : public ObjCClass<NSObject>
object_setInstanceVariable (_self, "cppObject", nullptr); object_setInstanceVariable (_self, "cppObject", nullptr);
} }
objc_super s = { _self, [NSObject class] };
ObjCMsgSendSuper<void> (&s, @selector(dealloc));
sendSuperclassMessage<void> (_self, @selector (dealloc));
} }
static ObjCLifetimeManagedClass objCLifetimeManagedClass; 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) 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) if (interfaceAddressInfo->sin_addr.s_addr != INADDR_NONE)
{ {
@@ -83,8 +83,8 @@ namespace
} }
else if (ifa->ifa_addr->sa_family == AF_INET6) 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; 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); 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: private:
ComClass* p = nullptr; 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 Android: JUCE_FALLTHROUGH
case iOS: 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_7: JUCE_FALLTHROUGH
case MacOSX_10_8: JUCE_FALLTHROUGH case MacOSX_10_8: JUCE_FALLTHROUGH
case MacOSX_10_9: 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()) if (! isRunning())
break; break;
Thread::yield();
Thread::sleep (1);
} }
else else
{ {


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

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


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

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


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

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


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

@@ -58,7 +58,6 @@
//============================================================================== //==============================================================================
#if defined (_WIN32) || defined (_WIN64) #if defined (_WIN32) || defined (_WIN64)
#define JUCE_WIN32 1
#define JUCE_WINDOWS 1 #define JUCE_WINDOWS 1
#elif defined (JUCE_ANDROID) #elif defined (JUCE_ANDROID)
#undef 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) static CharPointerType createUninitialisedBytes (size_t numBytes)
{ {
numBytes = (numBytes + 3) & ~(size_t) 3; 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->refCount.value = 0;
s->allocatedNumBytes = numBytes; s->allocatedNumBytes = numBytes;
return CharPointerType (s->text); return CharPointerType (s->text);
@@ -210,7 +210,7 @@ private:
static StringHolder* bufferFromText (const CharPointerType text) noexcept static StringHolder* bufferFromText (const CharPointerType text) noexcept
{ {
// (Can't use offsetof() here because of warnings about this not being a POD) // (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)); - (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); 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)) if (CharPointer_UTF16::isByteOrderMarkBigEndian (data))
{ {
@@ -2061,19 +2061,19 @@ struct StringEncodingConverter
template <> template <>
struct StringEncodingConverter<CharPointer_UTF8, CharPointer_UTF8> 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 <> template <>
struct StringEncodingConverter<CharPointer_UTF16, CharPointer_UTF16> 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 <> template <>
struct StringEncodingConverter<CharPointer_UTF32, CharPointer_UTF32> 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); } 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 // Annoyingly we can only forward-declare a typedef by forward-declaring the
// aliased type // aliased type
#if __has_attribute(objc_bridge) #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 ID: juce_data_structures
vendor: juce vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE data model helper classes name: JUCE data model helper classes
description: Classes for undo/redo management, and smart data structures. description: Classes for undo/redo management, and smart data structures.
website: http://www.juce.com/juce website: http://www.juce.com/juce


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

@@ -32,7 +32,7 @@
ID: juce_events ID: juce_events
vendor: juce vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE message and event handling classes name: JUCE message and event handling classes
description: Classes for running an application's main event loop and sending/receiving messages, timers, etc. description: Classes for running an application's main event loop and sending/receiving messages, timers, etc.
website: http://www.juce.com/juce 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) 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) if (userNotification != nil && userNotification.userInfo != nil)
didReceiveRemoteNotification (self, nil, [NSApplication sharedApplication], userNotification.userInfo); 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. */ /** Returns true if the font is underlined. */
bool isUnderlined() const noexcept; bool isUnderlined() const noexcept;
//============================================================================== //==============================================================================
/** Returns the font's horizontal scale. /** Returns the font's horizontal scale.
A value of 1.0 is the normal scale, less than this will be narrower, greater 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); static Font fromString (const String& fontDescription);
private: private:
//============================================================================== //==============================================================================
class SharedFontInternal; 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; } Point& operator/= (Point<OtherType> other) noexcept { *this = *this / other; return *this; }
/** Returns a point whose coordinates are multiplied by a given scalar value. */ /** 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. */ /** 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. */ /** Multiplies the point's coordinates by a scalar value. */
template <typename FloatType> template <typename FloatType>


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

@@ -75,10 +75,6 @@
#import <QuartzCore/QuartzCore.h> #import <QuartzCore/QuartzCore.h>
#import <CoreText/CoreText.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 #elif JUCE_LINUX
#ifndef JUCE_USE_FREETYPE #ifndef JUCE_USE_FREETYPE
#define JUCE_USE_FREETYPE 1 #define JUCE_USE_FREETYPE 1


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

@@ -35,7 +35,7 @@
ID: juce_graphics ID: juce_graphics
vendor: juce vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE graphics classes name: JUCE graphics classes
description: Classes for 2D vector graphics, image loading/saving, font handling, etc. description: Classes for 2D vector graphics, image loading/saving, font handling, etc.
website: http://www.juce.com/juce website: http://www.juce.com/juce
@@ -79,7 +79,7 @@
/** Config: JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING /** 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. find makes the text too 'fat' for their taste.
*/ */
#ifndef JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING #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); 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) CoreGraphicsContext::CoreGraphicsContext (CGContextRef c, float h)
: context (c), : context (c),
@@ -198,15 +207,18 @@ CoreGraphicsContext::CoreGraphicsContext (CGContextRef c, float h)
CGContextRetain (context.get()); CGContextRetain (context.get());
CGContextSaveGState (context.get()); CGContextSaveGState (context.get());
#if JUCE_MAC
bool enableFontSmoothing 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); CGContextSetShouldSmoothFonts (context.get(), enableFontSmoothing);
CGContextSetAllowsFontSmoothing (context.get(), enableFontSmoothing); CGContextSetAllowsFontSmoothing (context.get(), enableFontSmoothing);
#endif
CGContextSetShouldAntialias (context.get(), true); CGContextSetShouldAntialias (context.get(), true);
CGContextSetBlendMode (context.get(), kCGBlendModeNormal); CGContextSetBlendMode (context.get(), kCGBlendModeNormal);
rgbColourSpace.reset (CGColorSpaceCreateWithName (kCGColorSpaceSRGB)); rgbColourSpace.reset (CGColorSpaceCreateWithName (kCGColorSpaceSRGB));
@@ -455,26 +467,23 @@ void CoreGraphicsContext::fillCGRect (const CGRect& cgRect, bool replaceExisting
{ {
CGContextFillRect (context.get(), cgRect); CGContextFillRect (context.get(), cgRect);
} }
else if (state->fillType.isGradient())
{
CGContextSaveGState (context.get());
CGContextClipToRect (context.get(), cgRect);
drawGradient();
CGContextRestoreGState (context.get());
}
else else
{ {
CGContextSaveGState (context.get());
ScopedCGContextState scopedState (context.get());
CGContextClipToRect (context.get(), cgRect); 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) void CoreGraphicsContext::fillPath (const Path& path, const AffineTransform& transform)
{ {
CGContextSaveGState (context.get());
ScopedCGContextState scopedState (context.get());
if (state->fillType.isColour()) if (state->fillType.isColour())
{ {
@@ -501,8 +510,6 @@ void CoreGraphicsContext::fillPath (const Path& path, const AffineTransform& tra
else else
drawImage (state->fillType.image, state->fillType.transform, true); drawImage (state->fillType.image, state->fillType.transform, true);
} }
CGContextRestoreGState (context.get());
} }
void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTransform& transform) void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTransform& transform)
@@ -519,7 +526,7 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans
: rgbColourSpace.get(); : rgbColourSpace.get();
auto image = detail::ImagePtr { CoreGraphicsPixelData::getCachedImageRef (sourceImage, colourSpace) }; auto image = detail::ImagePtr { CoreGraphicsPixelData::getCachedImageRef (sourceImage, colourSpace) };
CGContextSaveGState (context.get());
ScopedCGContextState scopedState (context.get());
CGContextSetAlpha (context.get(), state->fillType.getOpacity()); CGContextSetAlpha (context.get(), state->fillType.getOpacity());
flip(); flip();
@@ -563,8 +570,6 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans
{ {
CGContextDrawImage (context.get(), imageRect, image.get()); 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); CGContextFillRects (context.get(), rects, num);
} }
else if (state->fillType.isGradient())
{
CGContextSaveGState (context.get());
CGContextClipToRects (context.get(), rects, num);
drawGradient();
CGContextRestoreGState (context.get());
}
else else
{ {
CGContextSaveGState (context.get());
ScopedCGContextState scopedState (context.get());
CGContextClipToRects (context.get(), rects, num); 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 else
{ {
CGContextSaveGState (context.get());
ScopedCGContextState scopedState (context.get());
flip(); flip();
applyTransform (transform); applyTransform (transform);
@@ -662,8 +664,6 @@ void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& tra
CGGlyph glyphs[1] = { (CGGlyph) glyphNumber }; CGGlyph glyphs[1] = { (CGGlyph) glyphNumber };
CGPoint positions[1] = { { 0.0f, 0.0f } }; CGPoint positions[1] = { { 0.0f, 0.0f } };
CGContextShowGlyphsAtPositions (context.get(), glyphs, positions, 1); CGContextShowGlyphsAtPositions (context.get(), glyphs, positions, 1);
CGContextRestoreGState (context.get());
} }
} }
else 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)); ctFontRef = getFontWithPointSize (ctFontRef, attr.font.getHeight() * getHeightToPointsFactor (ctFontRef));
CFAttributedStringSetAttribute (attribString, range, kCTFontAttributeName, 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(); auto extraKerning = attr.font.getExtraKerningFactor();
if (extraKerning != 0) if (extraKerning != 0)
@@ -463,6 +472,26 @@ namespace CoreTextTypeLayout
String::fromCFString (cfsFontStyle), String::fromCFString (cfsFontStyle),
(float) (CTFontGetSize (ctRunFont) / fontHeightToPointsFactor)); (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 (cfsFontStyle);
CFRelease (cfsFontFamily); 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; 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> template <typename PointOrRect>
static PointOrRect unscaledScreenPosToScaled (PointOrRect pos) noexcept static PointOrRect unscaledScreenPosToScaled (PointOrRect pos) noexcept
{ {
@@ -766,7 +782,7 @@ bool Component::isOpaque() const noexcept
//============================================================================== //==============================================================================
struct StandardCachedComponentImage : public CachedComponentImage struct StandardCachedComponentImage : public CachedComponentImage
{ {
StandardCachedComponentImage (Component& c) noexcept : owner (c), scale (1.0f) {}
StandardCachedComponentImage (Component& c) noexcept : owner (c) {}
void paint (Graphics& g) override void paint (Graphics& g) override
{ {
@@ -820,7 +836,7 @@ private:
Image image; Image image;
RectangleList<int> validArea; RectangleList<int> validArea;
Component& owner; Component& owner;
float scale;
float scale = 1.0f;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandardCachedComponentImage) 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>()); } Point<int> Component::getScreenPosition() const { return localPointToGlobal (Point<int>()); }
Rectangle<int> Component::getScreenBounds() const { return localAreaToGlobal (getLocalBounds()); } 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) 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> getLocalArea (const Component* sourceComponent,
Rectangle<int> areaRelativeToSourceComponent) const; 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. /** Converts a point relative to this component's top-left into a screen coordinate.
@see getLocalPoint, localAreaToGlobal @see getLocalPoint, localAreaToGlobal
*/ */
@@ -388,6 +401,15 @@ public:
*/ */
Rectangle<int> localAreaToGlobal (Rectangle<int> localArea) const; 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. /** 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)) if (parseNextNumber (d, num, false))
{ {
auto angle = degreesToRadians (num.getFloatValue());
auto angle = degreesToRadians (parseSafeFloat (num));
if (parseNextFlag (d, flagValue)) 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) forEachXmlChildElement (*xml, e)
{ {
@@ -618,7 +618,7 @@ private:
line.lineTo (x2, y2); 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 pointsAtt = xml->getStringAttribute ("points");
auto points = pointsAtt.getCharPointer(); auto points = pointsAtt.getCharPointer();
@@ -683,7 +683,7 @@ private:
//============================================================================== //==============================================================================
Drawable* parseShape (const XmlPath& xml, Path& path, Drawable* parseShape (const XmlPath& xml, Path& path,
const bool shouldParseTransform = true,
bool shouldParseTransform = true,
AffineTransform* additonalTransform = nullptr) const AffineTransform* additonalTransform = nullptr) const
{ {
if (shouldParseTransform && xml->hasAttribute ("transform")) if (shouldParseTransform && xml->hasAttribute ("transform"))
@@ -835,14 +835,14 @@ private:
auto col = parseColour (fillXml.getChild (e), "stop-color", Colours::black); auto col = parseColour (fillXml.getChild (e), "stop-color", Colours::black);
auto opacity = getStyleAttribute (fillXml.getChild (e), "stop-opacity", "1"); 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 ('%')) 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; result = true;
} }
} }
@@ -983,10 +983,10 @@ private:
float opacity = 1.0f; float opacity = 1.0f;
if (overallOpacity.isNotEmpty()) if (overallOpacity.isNotEmpty())
opacity = jlimit (0.0f, 1.0f, overallOpacity.getFloatValue());
opacity = jlimit (0.0f, 1.0f, parseSafeFloat (overallOpacity));
if (fillOpacity.isNotEmpty()) 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 fill (getStyleAttribute (xml, fillAttribute));
String urlID = parseURL (fill); String urlID = parseURL (fill);
@@ -1035,11 +1035,10 @@ private:
} }
//============================================================================== //==============================================================================
Drawable* useText (const XmlPath& xml) const 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 }; UseTextOp op = { this, &translation, nullptr };
@@ -1099,7 +1098,7 @@ private:
dt->setTransform (transform); dt->setTransform (transform);
dt->setColour (parseColour (xml, "fill", Colours::black) 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(), Rectangle<float> bounds (xCoords[0], yCoords[0] - font.getAscent(),
font.getStringWidthFloat (text), font.getHeight()); font.getStringWidthFloat (text), font.getHeight());
@@ -1138,8 +1137,8 @@ private:
//============================================================================== //==============================================================================
Drawable* useImage (const XmlPath& xml) const 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 }; UseImageOp op = { this, &translation, nullptr };
@@ -1210,10 +1209,13 @@ private:
setCommonAttributes (*di, xml); 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()))); 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; String number;
@@ -1251,13 +1253,13 @@ private:
return true; 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) return parseCoord (s, p.x, allowUnits, true)
&& parseCoord (s, p.y, allowUnits, false); && 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)) if (parseCoords (s, p, allowUnits))
return true; return true;
@@ -1268,8 +1270,8 @@ private:
float getCoordLength (const String& s, const float sizeForProportions) const noexcept 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) if (len > 2)
{ {
@@ -1293,7 +1295,7 @@ private:
return getCoordLength (xml->getStringAttribute (attName), sizeForProportions); 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(); auto text = list.getCharPointer();
float value; float value;
@@ -1302,6 +1304,12 @@ private:
coords.add (value); 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) void parseCSSStyle (const XmlPath& xml)
{ {
@@ -1452,7 +1460,7 @@ private:
return CharacterFunctions::isDigit (c) || c == '-' || c == '+'; 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; auto s = text;
@@ -1574,21 +1582,21 @@ private:
auto alpha = [&tokens, &text] auto alpha = [&tokens, &text]
{ {
if ((text.startsWith ("rgba") || text.startsWith ("hsla")) && tokens.size() == 4) if ((text.startsWith ("rgba") || text.startsWith ("hsla")) && tokens.size() == 4)
return tokens[3].getFloatValue();
return parseSafeFloat (tokens[3]);
return 1.0f; return 1.0f;
}(); }();
if (text.startsWith ("hsl")) 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); alpha);
if (tokens[0].containsChar ('%')) 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); alpha);
return Colour ((uint8) tokens[0].getIntValue(), return Colour ((uint8) tokens[0].getIntValue(),
@@ -1623,7 +1631,7 @@ private:
float numbers[6]; float numbers[6];
for (int i = 0; i < numElementsInArray (numbers); ++i) for (int i = 0; i < numElementsInArray (numbers); ++i)
numbers[i] = tokens[i].getFloatValue();
numbers[i] = parseSafeFloat (tokens[i]);
AffineTransform trans; 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)); currentPathBox.setColour (ComboBox::arrowColourId, findColour (currentPathBoxArrowColourId));
filenameBox.setColour (TextEditor::backgroundColourId, findColour (filenameBoxBackgroundColourId)); filenameBox.setColour (TextEditor::backgroundColourId, findColour (filenameBoxBackgroundColourId));
filenameBox.setColour (TextEditor::textColourId, findColour (filenameBoxTextColourId));
filenameBox.applyColourToAllText (findColour (filenameBoxTextColourId));
resized(); resized();
repaint(); repaint();


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

@@ -35,7 +35,7 @@
ID: juce_gui_basics ID: juce_gui_basics
vendor: juce vendor: juce
version: 6.0.0
version: 6.0.4
name: JUCE GUI core classes name: JUCE GUI core classes
description: Basic user-interface components and related classes. description: Basic user-interface components and related classes.
website: http://www.juce.com/juce website: http://www.juce.com/juce
@@ -43,7 +43,7 @@
dependencies: juce_graphics juce_data_structures dependencies: juce_graphics juce_data_structures
OSXFrameworks: Cocoa Carbon QuartzCore OSXFrameworks: Cocoa Carbon QuartzCore
iOSFrameworks: UIKit MobileCoreServices
iOSFrameworks: UIKit CoreServices
END_JUCE_MODULE_DECLARATION 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)) if (key.isKeyCode (KeyPress::downKey))
{ {
selectNextItem (1);
selectNextItem (MenuSelectionDirection::forwards);
} }
else if (key.isKeyCode (KeyPress::upKey)) else if (key.isKeyCode (KeyPress::upKey))
{ {
selectNextItem (-1);
selectNextItem (MenuSelectionDirection::backwards);
} }
else if (key.isKeyCode (KeyPress::leftKey)) else if (key.isKeyCode (KeyPress::leftKey))
{ {
@@ -414,14 +414,14 @@ struct MenuWindow : public Component
if (showSubMenuFor (currentChild)) if (showSubMenuFor (currentChild))
{ {
if (isSubMenuVisible()) if (isSubMenuVisible())
activeSubMenu->selectNextItem (0);
activeSubMenu->selectNextItem (MenuSelectionDirection::current);
} }
else if (componentAttachedTo != nullptr) else if (componentAttachedTo != nullptr)
{ {
componentAttachedTo->keyPressed (key); componentAttachedTo->keyPressed (key);
} }
} }
else if (key.isKeyCode (KeyPress::returnKey))
else if (key.isKeyCode (KeyPress::returnKey) || key.isKeyCode (KeyPress::spaceKey))
{ {
triggerCurrentlyHighlightedItem(); triggerCurrentlyHighlightedItem();
} }
@@ -948,24 +948,46 @@ struct MenuWindow : public Component
} }
} }
void selectNextItem (int delta)
enum class MenuSelectionDirection
{
forwards,
backwards,
current
};
void selectNextItem (MenuSelectionDirection direction)
{ {
disableTimerUntilMouseMoves(); 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;) 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 (auto* mic = items.getUnchecked ((start + items.size()) % items.size()))
{ {
if (canBeTriggered (mic->item) || hasActiveSubMenu (mic->item)) if (canBeTriggered (mic->item) || hasActiveSubMenu (mic->item))
{ {
setCurrentlyHighlightedChild (mic); 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 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 ineffective unless you have a JUCE Indie or Pro license, or are using JUCE
under the GPL v3 license. 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 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 ineffective unless you have a JUCE Indie or Pro license, or are using JUCE
under the GPL v3 license. 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); setBounds (bounds);
setAlwaysOnTop (true); setAlwaysOnTop (true);
setVisible (true);
addToDesktop (0); addToDesktop (0);
enterModalState (true, enterModalState (true,


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

@@ -26,19 +26,22 @@
namespace juce namespace juce
{ {
class FileChooser::Native : private Component,
public FileChooser::Pimpl
class FileChooser::Native : public FileChooser::Pimpl,
public Component,
private AsyncUpdater
{ {
public: public:
Native (FileChooser& fileChooser, int flags) Native (FileChooser& fileChooser, int flags)
: owner (fileChooser) : 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); FileChooserDelegateClass::setOwner (delegate.get(), this);
static FileChooserControllerClass controllerClass;
auto* controllerClassInstance = controllerClass.createInstance();
String firstFileExtension;
auto utTypeArray = createNSArrayFromStringArray (getUTTypesForWildcards (owner.filters, firstFileExtension)); auto utTypeArray = createNSArrayFromStringArray (getUTTypesForWildcards (owner.filters, firstFileExtension));
if ((flags & FileBrowserComponent::saveMode) != 0) if ((flags & FileBrowserComponent::saveMode) != 0)
@@ -69,47 +72,51 @@ public:
} }
auto url = [[NSURL alloc] initFileURLWithPath: juceStringToNS (currentFileOrDirectory.getFullPathName())]; 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]; [url release];
} }
else 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() setDelegate: delegate.get()];
[controller.get() setModalTransitionStyle: UIModalTransitionStyleCrossDissolve]; [controller.get() setModalTransitionStyle: UIModalTransitionStyleCrossDissolve];
setOpaque (false); 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 // 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 // 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! // specify a parent component in the FileChooser's constructor!
jassert (fileChooser.parent != nullptr);
jassertfalse;
return;
} }
}
else
{
auto chooserBounds = Desktop::getInstance().getDisplays().getMainDisplay().userArea; auto chooserBounds = Desktop::getInstance().getDisplays().getMainDisplay().userArea;
setBounds (chooserBounds); setBounds (chooserBounds);
setAlwaysOnTop (true); setAlwaysOnTop (true);
setVisible (true);
addToDesktop (0); addToDesktop (0);
} }
} }
@@ -131,8 +138,6 @@ public:
#endif #endif
} }
private:
//==============================================================================
void parentHierarchyChanged() override void parentHierarchyChanged() override
{ {
auto* newPeer = dynamic_cast<UIViewComponentPeer*> (getPeer()); auto* newPeer = dynamic_cast<UIViewComponentPeer*> (getPeer());
@@ -141,11 +146,23 @@ private:
{ {
peer = newPeer; 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) static StringArray getUTTypesForWildcards (const String& filterWildcards, String& firstExtension)
{ {
@@ -182,7 +199,9 @@ private:
} }
} }
else else
{
result.add ("public.data"); result.add ("public.data");
}
return result; return result;
} }
@@ -207,6 +226,8 @@ private:
//============================================================================== //==============================================================================
void didPickDocumentAtURL (NSURL* url) void didPickDocumentAtURL (NSURL* url)
{ {
cancelPendingUpdate();
bool isWriting = controller.get().documentPickerMode == UIDocumentPickerModeExportToService bool isWriting = controller.get().documentPickerMode == UIDocumentPickerModeExportToService
| controller.get().documentPickerMode == UIDocumentPickerModeMoveToService; | controller.get().documentPickerMode == UIDocumentPickerModeMoveToService;
@@ -267,9 +288,9 @@ private:
void pickerWasCancelled() void pickerWasCancelled()
{ {
Array<URL> chooserResults;
cancelPendingUpdate();
owner.finished (chooserResults);
owner.finished ({});
exitModalState (0); exitModalState (0);
} }
@@ -294,21 +315,40 @@ private:
//============================================================================== //==============================================================================
static void didPickDocumentAtURL (id self, SEL, UIDocumentPickerViewController*, NSURL* url) static void didPickDocumentAtURL (id self, SEL, UIDocumentPickerViewController*, NSURL* url)
{ {
auto picker = getOwner (self);
if (picker != nullptr)
if (auto* picker = getOwner (self))
picker->didPickDocumentAtURL (url); picker->didPickDocumentAtURL (url);
} }
static void documentPickerWasCancelled (id self, SEL, UIDocumentPickerViewController*) static void documentPickerWasCancelled (id self, SEL, UIDocumentPickerViewController*)
{ {
auto picker = getOwner (self);
if (picker != nullptr)
if (auto* picker = getOwner (self))
picker->pickerWasCancelled(); 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; FileChooser& owner;
std::unique_ptr<NSObject<UIDocumentPickerDelegate>, NSObjectDeleter> delegate; std::unique_ptr<NSObject<UIDocumentPickerDelegate>, NSObjectDeleter> delegate;
@@ -316,6 +356,7 @@ private:
UIViewComponentPeer* peer = nullptr; UIViewComponentPeer* peer = nullptr;
static FileChooserDelegateClass fileChooserDelegateClass; static FileChooserDelegateClass fileChooserDelegateClass;
static FileChooserControllerClass fileChooserControllerClass;
//============================================================================== //==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Native) 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; 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() static UIInterfaceOrientation getWindowOrientation()
{ {
UIApplication* sharedApplication = [UIApplication sharedApplication]; UIApplication* sharedApplication = [UIApplication sharedApplication];
@@ -79,28 +71,11 @@ namespace Orientations
return UIInterfaceOrientationPortrait; 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() static NSUInteger getSupportedOrientations()
{ {
NSUInteger allowed = 0; NSUInteger allowed = 0;
Desktop& d = Desktop::getInstance();
auto& d = Desktop::getInstance();
if (d.isOrientationEnabled (Desktop::upright)) allowed |= UIInterfaceOrientationMaskPortrait; if (d.isOrientationEnabled (Desktop::upright)) allowed |= UIInterfaceOrientationMaskPortrait;
if (d.isOrientationEnabled (Desktop::upsideDown)) allowed |= UIInterfaceOrientationMaskPortraitUpsideDown; if (d.isOrientationEnabled (Desktop::upsideDown)) allowed |= UIInterfaceOrientationMaskPortraitUpsideDown;
@@ -200,12 +175,10 @@ namespace juce
struct UIViewPeerControllerReceiver struct UIViewPeerControllerReceiver
{ {
virtual ~UIViewPeerControllerReceiver();
virtual ~UIViewPeerControllerReceiver() = default;
virtual void setViewController (UIViewController*) = 0; virtual void setViewController (UIViewController*) = 0;
}; };
UIViewPeerControllerReceiver::~UIViewPeerControllerReceiver() {}
class UIViewComponentPeer : public ComponentPeer, class UIViewComponentPeer : public ComponentPeer,
public FocusChangeListener, public FocusChangeListener,
public UIViewPeerControllerReceiver public UIViewPeerControllerReceiver
@@ -259,7 +232,7 @@ public:
void updateHiddenTextContent (TextInputTarget*); void updateHiddenTextContent (TextInputTarget*);
void globalFocusChanged (Component*) override; void globalFocusChanged (Component*) override;
void updateTransformAndScreenBounds();
void updateScreenBounds();
void handleTouches (UIEvent*, bool isDown, bool isUp, bool isCancel); void handleTouches (UIEvent*, bool isDown, bool isUp, bool isCancel);
@@ -268,10 +241,11 @@ public:
void performAnyPendingRepaintsNow() override; 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 static int64 getMouseTime (UIEvent* e) noexcept
{ {
@@ -279,73 +253,10 @@ public:
+ (int64) ([e timestamp] * 1000.0); + (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; static MultiTouchMapper<UITouch*> currentTouches;
private: private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponentPeer)
//==============================================================================
class AsyncRepaintMessage : public CallbackMessage class AsyncRepaintMessage : public CallbackMessage
{ {
public: public:
@@ -363,6 +274,9 @@ private:
peer->repaint (rect); peer->repaint (rect);
} }
}; };
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponentPeer)
}; };
static void sendScreenBoundsUpdate (JuceUIViewController* c) static void sendScreenBoundsUpdate (JuceUIViewController* c)
@@ -370,7 +284,7 @@ static void sendScreenBoundsUpdate (JuceUIViewController* c)
JuceUIView* juceView = (JuceUIView*) [c view]; JuceUIView* juceView = (JuceUIView*) [c view];
if (juceView != nil && juceView->owner != nullptr) if (juceView != nil && juceView->owner != nullptr)
juceView->owner->updateTransformAndScreenBounds();
juceView->owner->updateScreenBounds();
} }
void AsyncBoundsUpdater::handleAsyncUpdate() void AsyncBoundsUpdater::handleAsyncUpdate()
@@ -614,14 +528,9 @@ bool KeyPress::isKeyCurrentlyDown (int)
Point<float> juce_lastMousePos; Point<float> juce_lastMousePos;
//============================================================================== //==============================================================================
UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyleFlags, UIView* viewToAttachTo)
UIViewComponentPeer::UIViewComponentPeer (Component& comp, int windowStyleFlags, UIView* viewToAttachTo)
: ComponentPeer (comp, windowStyleFlags), : ComponentPeer (comp, windowStyleFlags),
window (nil),
view (nil),
controller (nil),
isSharedWindow (viewToAttachTo != nil), isSharedWindow (viewToAttachTo != nil),
fullScreen (false),
insideDrawRect (false),
isAppex (SystemStats::isRunningInAppExtensionSandbox()) isAppex (SystemStats::isRunningInAppExtensionSandbox())
{ {
CGRect r = convertToCGRect (component.getBounds()); CGRect r = convertToCGRect (component.getBounds());
@@ -632,7 +541,6 @@ UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyle
view.hidden = true; view.hidden = true;
view.opaque = component.isOpaque(); view.opaque = component.isOpaque();
view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent: 0]; view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent: 0];
view.transform = CGAffineTransformIdentity;
if (isSharedWindow) if (isSharedWindow)
{ {
@@ -641,7 +549,7 @@ UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyle
} }
else else
{ {
r = convertToCGRect (rotatedScreenPosToReal (component.getBounds()));
r = convertToCGRect (component.getBounds());
r.origin.y = [UIScreen mainScreen].bounds.size.height - (r.origin.y + r.size.height); r.origin.y = [UIScreen mainScreen].bounds.size.height - (r.origin.y + r.size.height);
window = [[JuceUIWindow alloc] initWithFrame: r]; window = [[JuceUIWindow alloc] initWithFrame: r];
@@ -652,7 +560,6 @@ UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyle
window.rootViewController = controller; window.rootViewController = controller;
window.hidden = true; window.hidden = true;
window.transform = Orientations::getCGTransformFor (Desktop::getInstance().getCurrentOrientation());
window.opaque = component.isOpaque(); window.opaque = component.isOpaque();
window.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent: 0]; window.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent: 0];
@@ -660,8 +567,6 @@ UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyle
window.windowLevel = UIWindowLevelAlert; window.windowLevel = UIWindowLevelAlert;
view.frame = CGRectMake (0, 0, r.size.width, r.size.height); view.frame = CGRectMake (0, 0, r.size.width, r.size.height);
[window addSubview: view];
} }
setTitle (component.getName()); setTitle (component.getName());
@@ -718,7 +623,7 @@ void UIViewComponentPeer::setBounds (const Rectangle<int>& newBounds, const bool
} }
else else
{ {
window.frame = convertToCGRect (rotatedScreenPosToReal (newBounds));
window.frame = convertToCGRect (newBounds);
view.frame = CGRectMake (0, 0, (CGFloat) newBounds.getWidth(), (CGFloat) newBounds.getHeight()); view.frame = CGRectMake (0, 0, (CGFloat) newBounds.getWidth(), (CGFloat) newBounds.getHeight());
handleMovedOrResized(); handleMovedOrResized();
@@ -733,8 +638,6 @@ Rectangle<int> UIViewComponentPeer::getBounds (const bool global) const
{ {
r = [view convertRect: r toView: view.window]; r = [view convertRect: r toView: view.window];
r = [view.window convertRect: r toWindow: nil]; r = [view.window convertRect: r toWindow: nil];
return realScreenPosToRotated (convertToRectInt (r));
} }
return convertToRectInt (r); return convertToRectInt (r);
@@ -759,8 +662,8 @@ void UIViewComponentPeer::setFullScreen (bool shouldBeFullScreen)
{ {
if (! isSharedWindow) 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()) if ((! shouldBeFullScreen) && r.isEmpty())
r = getBounds(); 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) if (fullScreen)
{ {
@@ -792,13 +693,13 @@ void UIViewComponentPeer::updateTransformAndScreenBounds()
else if (! isSharedWindow) else if (! isSharedWindow)
{ {
// this will re-centre the window, but leave its size unchanged // 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)); component.setBounds (oldArea.withPosition (x, y));
} }
@@ -808,13 +709,8 @@ void UIViewComponentPeer::updateTransformAndScreenBounds()
bool UIViewComponentPeer::contains (Point<int> localPos, bool trueIfInAChildWindow) const 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) UIView* v = [view hitTest: convertToCGPoint (localPos)
withEvent: nil]; withEvent: nil];
@@ -844,16 +740,10 @@ void UIViewComponentPeer::toFront (bool makeActiveWindow)
void UIViewComponentPeer::toBehind (ComponentPeer* other) void UIViewComponentPeer::toBehind (ComponentPeer* other)
{ {
if (UIViewComponentPeer* const otherPeer = dynamic_cast<UIViewComponentPeer*> (other))
if (auto* otherPeer = dynamic_cast<UIViewComponentPeer*> (other))
{ {
if (isSharedWindow) if (isSharedWindow)
{
[[view superview] insertSubview: view belowSubview: otherPeer->view]; [[view superview] insertSubview: view belowSubview: otherPeer->view];
}
else
{
// don't know how to do this
}
} }
else else
{ {
@@ -869,23 +759,17 @@ void UIViewComponentPeer::setIcon (const Image& /*newIcon*/)
//============================================================================== //==============================================================================
static float getMaximumTouchForce (UITouch* touch) noexcept static float getMaximumTouchForce (UITouch* touch) noexcept
{ {
#if defined (__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
if ([touch respondsToSelector: @selector (maximumPossibleForce)]) if ([touch respondsToSelector: @selector (maximumPossibleForce)])
return (float) touch.maximumPossibleForce; return (float) touch.maximumPossibleForce;
#endif
ignoreUnused (touch);
return 0.0f; return 0.0f;
} }
static float getTouchForce (UITouch* touch) noexcept static float getTouchForce (UITouch* touch) noexcept
{ {
#if defined (__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
if ([touch respondsToSelector: @selector (force)]) if ([touch respondsToSelector: @selector (force)])
return (float) touch.force; return (float) touch.force;
#endif
ignoreUnused (touch);
return 0.0f; 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) for (unsigned int i = 0; i < [touches count]; ++i)
{ {
UITouch* touch = [touches objectAtIndex: i]; UITouch* touch = [touches objectAtIndex: i];
const float maximumForce = getMaximumTouchForce (touch);
auto maximumForce = getMaximumTouchForce (touch);
if ([touch phase] == UITouchPhaseStationary && maximumForce <= 0) if ([touch phase] == UITouchPhaseStationary && maximumForce <= 0)
continue; continue;
CGPoint p = [touch locationInView: view]; 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(); 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) 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: // 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, handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, modsToSend, pressure,
MouseInputSource::invalidOrientation, time, { }, touchIndex); 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 static UIKeyboardType getUIKeyboardType (TextInputTarget::VirtualKeyboardType type) noexcept
{ {
switch (type) switch (type)
{ {
case TextInputTarget::textKeyboard: return UIKeyboardTypeAlphabet; 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::urlKeyboard: return UIKeyboardTypeURL;
case TextInputTarget::emailAddressKeyboard: return UIKeyboardTypeEmailAddress; case TextInputTarget::emailAddressKeyboard: return UIKeyboardTypeEmailAddress;
case TextInputTarget::phoneNumberKeyboard: return UIKeyboardTypePhonePad; case TextInputTarget::phoneNumberKeyboard: return UIKeyboardTypePhonePad;
@@ -1041,9 +920,9 @@ void UIViewComponentPeer::updateHiddenTextContent (TextInputTarget* target)
BOOL UIViewComponentPeer::textViewReplaceCharacters (Range<int> range, const String& text) 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 (range.getLength() == 1 && text.isEmpty()) // (detect backspace)
if (currentSelection.isEmpty()) if (currentSelection.isEmpty())
@@ -1062,15 +941,16 @@ BOOL UIViewComponentPeer::textViewReplaceCharacters (Range<int> range, const Str
void UIViewComponentPeer::globalFocusChanged (Component*) 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 else
{ {
@@ -1078,7 +958,6 @@ void UIViewComponentPeer::globalFocusChanged (Component*)
} }
} }
//============================================================================== //==============================================================================
void UIViewComponentPeer::drawRect (CGRect r) void UIViewComponentPeer::drawRect (CGRect r)
{ {
@@ -1091,9 +970,6 @@ void UIViewComponentPeer::drawRect (CGRect r)
CGContextClearRect (cg, CGContextGetClipBoundingBox (cg)); CGContextClearRect (cg, CGContextGetClipBoundingBox (cg));
CGContextConcatCTM (cg, CGAffineTransformMake (1, 0, 0, -1, 0, getComponent().getHeight())); 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()); CoreGraphicsContext g (cg, getComponent().getHeight());
insideDrawRect = true; insideDrawRect = true;
@@ -1111,9 +987,9 @@ void Desktop::setKioskComponent (Component* kioskModeComp, bool enableOrDisable,
{ {
displays->refresh(); 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]; [uiViewPeer->controller setNeedsStatusBarAppearanceUpdate];
peer->setFullScreen (enableOrDisable); peer->setFullScreen (enableOrDisable);
@@ -1125,21 +1001,18 @@ void Desktop::allowedOrientationsChanged()
// if the current orientation isn't allowed anymore then switch orientations // if the current orientation isn't allowed anymore then switch orientations
if (! isOrientationEnabled (getCurrentOrientation())) 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"]; [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
[value release]; [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 } // 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 struct AppInactivityCallback // NB: careful, this declaration is duplicated in other modules
{ {
virtual ~AppInactivityCallback() {}
virtual ~AppInactivityCallback() = default;
virtual void appBecomingInactive() = 0; 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 class iOSMessageBox
{ {
public: public:
@@ -466,7 +442,6 @@ public:
ModalComponentManager::Callback* cb, const bool async) ModalComponentManager::Callback* cb, const bool async)
: result (0), resultReceived (false), callback (cb), isAsync (async) : result (0), resultReceived (false), callback (cb), isAsync (async)
{ {
#if JUCE_USE_NEW_IOS_ALERTWINDOW
if (currentlyFocusedPeer != nullptr) if (currentlyFocusedPeer != nullptr)
{ {
UIAlertController* alert = [UIAlertController alertControllerWithTitle: juceStringToNS (title) UIAlertController* alert = [UIAlertController alertControllerWithTitle: juceStringToNS (title)
@@ -486,27 +461,6 @@ public:
// have at least one window on screen when you use this // have at least one window on screen when you use this
jassertfalse; 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() int getResult()
@@ -515,11 +469,7 @@ public:
JUCE_AUTORELEASEPOOL JUCE_AUTORELEASEPOOL
{ {
#if JUCE_USE_NEW_IOS_ALERTWINDOW
while (! resultReceived) while (! resultReceived)
#else
while (! (alert.hidden || resultReceived))
#endif
[[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
} }
@@ -544,7 +494,6 @@ private:
std::unique_ptr<ModalComponentManager::Callback> callback; std::unique_ptr<ModalComponentManager::Callback> callback;
const bool isAsync; const bool isAsync;
#if JUCE_USE_NEW_IOS_ALERTWINDOW
void addButton (UIAlertController* alert, NSString* text, int index) void addButton (UIAlertController* alert, NSString* text, int index)
{ {
if (text != nil) if (text != nil)
@@ -552,33 +501,10 @@ private:
style: UIAlertActionStyleDefault style: UIAlertActionStyleDefault
handler: ^(UIAlertAction*) { this->buttonClicked (index); }]]; handler: ^(UIAlertAction*) { this->buttonClicked (index); }]];
} }
#else
UIAlertView* alert;
JuceAlertBoxDelegate* delegate;
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSMessageBox) 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 #if JUCE_MODAL_LOOPS_PERMITTED
void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType /*iconType*/, void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType /*iconType*/,
@@ -742,13 +668,9 @@ void Displays::findDisplays (float masterScale)
UIScreen* s = [UIScreen mainScreen]; UIScreen* s = [UIScreen mainScreen];
Display d; Display d;
d.userArea = d.totalArea = UIViewComponentPeer::realScreenPosToRotated (convertToRectInt ([s bounds])) / masterScale;
d.userArea = d.totalArea = convertToRectInt ([s bounds]) / masterScale;
d.isMain = true; 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; d.dpi = 160 * d.scale;
displays.add (d); 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 #if JUCE_MODAL_LOOPS_PERMITTED
static bool exeIsAvailable (const char* const executable)
static bool exeIsAvailable (String executable)
{ {
ChildProcess child; 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, class FileChooser::Native : public FileChooser::Pimpl,
private Timer private Timer
@@ -68,7 +70,7 @@ public:
child.start (args, ChildProcess::wantStdOut); child.start (args, ChildProcess::wantStdOut);
while (child.isRunning()) while (child.isRunning())
if (! MessageManager::getInstance()->runDispatchLoopUntil(20))
if (! MessageManager::getInstance()->runDispatchLoopUntil (20))
break; break;
finish (false); finish (false);
@@ -187,7 +189,7 @@ private:
} }
args.add (startPath.getFullPathName()); args.add (startPath.getFullPathName());
args.add (owner.filters.replaceCharacter (';', ' '));
args.add ("(" + owner.filters.replaceCharacter (';', ' ') + ")");
} }
void addZenityArgs() void addZenityArgs()
@@ -218,8 +220,7 @@ private:
StringArray tokens; StringArray tokens;
tokens.addTokens (owner.filters, ";,|", "\""); 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()) 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 class LinuxRepaintManager : public Timer
{ {
public: public:
LinuxRepaintManager (LinuxComponentPeer& p) : peer (p) {}
LinuxRepaintManager (LinuxComponentPeer& p)
: peer (p),
isSemiTransparentWindow ((peer.getStyleFlags() & ComponentPeer::windowIsSemiTransparent) != 0)
{
}
void timerCallback() override void timerCallback() override
{ {
if (XWindowSystem::getInstance()->getNumPaintsPending (peer.windowH) > 0)
XWindowSystem::getInstance()->processPendingPaintsForWindow (peer.windowH);
if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
return; return;
if (! regionsNeedingRepaint.isEmpty()) if (! regionsNeedingRepaint.isEmpty())
@@ -359,7 +365,7 @@ private:
void performAnyPendingRepaintsNow() void performAnyPendingRepaintsNow()
{ {
if (XWindowSystem::getInstance()->getNumPaintsPending (peer.windowH) > 0)
if (XWindowSystem::getInstance()->getNumPaintsPendingForWindow (peer.windowH) > 0)
{ {
startTimer (repaintTimerPeriod); startTimer (repaintTimerPeriod);
return; return;
@@ -374,7 +380,8 @@ private:
if (image.isNull() || image.getWidth() < totalArea.getWidth() if (image.isNull() || image.getWidth() < totalArea.getWidth()
|| image.getHeight() < totalArea.getHeight()) || image.getHeight() < totalArea.getHeight())
{ {
image = XWindowSystem::getInstance()->createImage (totalArea.getWidth(), totalArea.getHeight(),
image = XWindowSystem::getInstance()->createImage (isSemiTransparentWindow,
totalArea.getWidth(), totalArea.getHeight(),
useARGBImagesForRendering); useARGBImagesForRendering);
} }
@@ -407,6 +414,7 @@ private:
enum { repaintTimerPeriod = 1000 / 100 }; enum { repaintTimerPeriod = 1000 / 100 };
LinuxComponentPeer& peer; LinuxComponentPeer& peer;
const bool isSemiTransparentWindow;
Image image; Image image;
uint32 lastTimeImageUsed = 0; uint32 lastTimeImageUsed = 0;
RectangleList<int> regionsNeedingRepaint; 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> struct DelegateClass : ObjCClass<DelegateType>
{ {
DelegateClass() : ObjCClass <DelegateType> ("JUCEFileChooser_")
DelegateClass() : ObjCClass<DelegateType> ("JUCEFileChooser_")
{ {
addIvar<Native*> ("cppObject"); 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"); auto owner = getIvar<JuceMainMenuHandler*> (self, "owner");
if (auto* juceItem = getJuceClassFromNSObject<PopupMenu::Item> ([item representedObject])) 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])); owner->invoke (*juceItem, static_cast<int> ([item tag]));
}
} }
static void menuNeedsUpdate (id self, SEL, NSMenu* menu) 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) \ #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 && USE_COREGRAPHICS_RENDERING && JUCE_COREGRAPHICS_DRAW_ASYNC
@@ -680,8 +685,8 @@ public:
} }
else if ([ev respondsToSelector: @selector (deviceDeltaX)]) 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 (...) @catch (...)
@@ -1770,10 +1775,7 @@ private:
owner->stringBeingComposed.clear(); owner->stringBeingComposed.clear();
if (! (owner->textWasInserted || owner->redirectKeyDown (ev))) 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); 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) static void becomeKeyWindow (id self, SEL)
{ {
sendSuperclassMessage (self, @selector (becomeKeyWindow));
sendSuperclassMessage<void> (self, @selector (becomeKeyWindow));
if (auto* owner = getOwner (self)) if (auto* owner = getOwner (self))
{ {
@@ -2057,10 +2056,15 @@ private:
return owner == nullptr || owner->windowShouldClose(); 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)) if (auto* owner = getOwner (self))
{
frameRect = sendSuperclassMessage<NSRect, NSRect, NSScreen*> (self, @selector (constrainFrameRect:toScreen:),
frameRect, screen);
frameRect = owner->constrainRect (frameRect); frameRect = owner->constrainRect (frameRect);
}
return frameRect; return frameRect;
} }
@@ -2104,10 +2108,10 @@ private:
{ {
if (auto* owner = getOwner (self)) 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(); 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<std::function<void()>*> (self, "callback");
delete getIvar<NSDragOperation*> (self, "operation"); 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) 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 // Handle nonexistent root directories in the same way as existing ones
files.calloc (static_cast<size_t> (charsAvailableForResult) + 1); files.calloc (static_cast<size_t> (charsAvailableForResult) + 1);
if (startingFile.isDirectory() ||startingFile.isRoot())
if (startingFile.isDirectory() || startingFile.isRoot())
{ {
initialPath = startingFile.getFullPathName(); initialPath = startingFile.getFullPathName();
} }
@@ -154,7 +154,7 @@ private:
Component::SafePointer<Component> owner; Component::SafePointer<Component> owner;
String title, filtersString; String title, filtersString;
std::unique_ptr<CustomComponentHolder> customComponent; std::unique_ptr<CustomComponentHolder> customComponent;
String initialPath, returnedString, defaultExtension;
String initialPath, returnedString;
WaitableEvent threadHasReference; WaitableEvent threadHasReference;
CriticalSection deletingDialog; CriticalSection deletingDialog;
@@ -167,8 +167,144 @@ private:
Atomic<HWND> nativeDialogRef; Atomic<HWND> nativeDialogRef;
Atomic<int> shouldCancel; 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; Array<URL> selections;
@@ -213,7 +349,7 @@ private:
{ {
OPENFILENAMEW of = {}; OPENFILENAMEW of = {};
#ifdef OPENFILENAME_SIZE_VERSION_400W
#ifdef OPENFILENAME_SIZE_VERSION_400W
of.lStructSize = OPENFILENAME_SIZE_VERSION_400W; of.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
#else #else
of.lStructSize = sizeof (of); of.lStructSize = sizeof (of);
@@ -231,16 +367,10 @@ private:
if (isSave) 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)) if (! GetSaveFileName (&of))
return {}; return {};
@@ -251,7 +381,7 @@ private:
return {}; 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; const WCHAR* filename = files + of.nFileOffset;
@@ -267,18 +397,34 @@ private:
} }
} }
getNativeDialogList().removeValue (this);
return selections; 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 void run() override
{ {
// as long as the thread is running, don't delete this class // as long as the thread is running, don't delete this class
Ptr safeThis (this); Ptr safeThis (this);
threadHasReference.signal(); threadHasReference.signal();
Array<URL> r = openDialog (true);
auto r = openDialog (true);
MessageManager::callAsync ([safeThis, r] MessageManager::callAsync ([safeThis, r]
{ {
safeThis->results = r; safeThis->results = r;
@@ -330,6 +476,23 @@ private:
return ofFlags; 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) void initialised (HWND hWnd)
{ {
@@ -414,7 +577,7 @@ private:
if (customComponent != nullptr && shouldCancel.get() == 0) 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 }; WCHAR path [MAX_PATH * 2] = { 0 };
CommDlg_OpenSave_GetFilePath (hdlg, (LPARAM) &path, MAX_PATH); CommDlg_OpenSave_GetFilePath (hdlg, (LPARAM) &path, MAX_PATH);
@@ -514,7 +677,6 @@ class FileChooser::Native : public Component,
public FileChooser::Pimpl public FileChooser::Pimpl
{ {
public: public:
Native (FileChooser& fileChooser, int flags, FilePreviewComponent* previewComp) Native (FileChooser& fileChooser, int flags, FilePreviewComponent* previewComp)
: owner (fileChooser), : owner (fileChooser),
nativeFileChooser (new Win32NativeFileChooser (this, flags, previewComp, fileChooser.startingFile, nativeFileChooser (new Win32NativeFileChooser (this, flags, previewComp, fileChooser.startingFile,
@@ -531,7 +693,7 @@ public:
addToDesktop (0); addToDesktop (0);
} }
~Native()
~Native() override
{ {
exitModalState (0); exitModalState (0);
nativeFileChooser->cancel(); nativeFileChooser->cancel();


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

@@ -3176,10 +3176,7 @@ private:
updateKeyModifiers(); updateKeyModifiers();
if (hwnd == GetActiveWindow()) if (hwnd == GetActiveWindow())
{
handleKeyPress (key, 0);
return true;
}
return handleKeyPress (key, 0);
} }
return false; 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(); srcMimeTypeAtomList.clear();
dragAndDropCurrentMimeType = 0; 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) 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; using namespace X11SymbolHelpers;
if (! loadSymbols (xLib, xextLib, if (! loadSymbols (xLib, xextLib,
makeSymbolBinding (xAllocClassHint, "XAllocClassHint"),
makeSymbolBinding (xAllocSizeHints, "XAllocSizeHints"), makeSymbolBinding (xAllocSizeHints, "XAllocSizeHints"),
makeSymbolBinding (xAllocWMHints, "XAllocWMHints"), makeSymbolBinding (xAllocWMHints, "XAllocWMHints"),
makeSymbolBinding (xBitmapBitOrder, "XBitmapBitOrder"), makeSymbolBinding (xBitmapBitOrder, "XBitmapBitOrder"),
@@ -169,6 +170,7 @@ bool X11Symbols::loadAllSymbols()
makeSymbolBinding (xScreenNumberOfScreen, "XScreenNumberOfScreen"), makeSymbolBinding (xScreenNumberOfScreen, "XScreenNumberOfScreen"),
makeSymbolBinding (xSelectInput, "XSelectInput"), makeSymbolBinding (xSelectInput, "XSelectInput"),
makeSymbolBinding (xSendEvent, "XSendEvent"), makeSymbolBinding (xSendEvent, "XSendEvent"),
makeSymbolBinding (xSetClassHint, "XSetClassHint"),
makeSymbolBinding (xSetErrorHandler, "XSetErrorHandler"), makeSymbolBinding (xSetErrorHandler, "XSetErrorHandler"),
makeSymbolBinding (xSetIOErrorHandler, "XSetIOErrorHandler"), makeSymbolBinding (xSetIOErrorHandler, "XSetIOErrorHandler"),
makeSymbolBinding (xSetInputFocus, "XSetInputFocus"), 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(); bool loadAllSymbols();
//============================================================================== //==============================================================================
JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XAllocClassHint, xAllocClassHint,
(),
XClassHint*)
JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XAllocSizeHints, xAllocSizeHints, JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XAllocSizeHints, xAllocSizeHints,
(), (),
XSizeHints*) XSizeHints*)
@@ -405,6 +409,10 @@ public:
(::Display*, ::Window, Bool, long, XEvent*), (::Display*, ::Window, Bool, long, XEvent*),
Status) Status)
JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XSetClassHint, xSetClassHint,
(::Display*, ::Window, XClassHint*),
void)
JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XSetErrorHandler, xSetErrorHandler, JUCE_GENERATE_FUNCTION_WITH_DEFAULT (XSetErrorHandler, xSetErrorHandler,
(XErrorHandler), (XErrorHandler),
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 =============================== //================================= X11 - Bitmap ===============================
static std::unordered_map<::Window, int> shmPaintsPendingMap;
class XBitmapImage : public ImagePixelData class XBitmapImage : public ImagePixelData
{ {
public: 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) bool clearImage, unsigned int imageDepth_, Visual* visual)
: ImagePixelData (format, w, h), : ImagePixelData (format, w, h),
imageDepth (imageDepth_),
display (d)
imageDepth (imageDepth_)
{ {
jassert (format == Image::RGB || format == Image::ARGB); jassert (format == Image::RGB || format == Image::ARGB);
@@ -807,6 +804,11 @@ public:
{ {
XWindowSystemUtilities::ScopedXLock xLock; XWindowSystemUtilities::ScopedXLock xLock;
#if JUCE_USE_XSHM
if (isUsingXShm())
XWindowSystem::getInstance()->addPendingPaintForWindow (window);
#endif
if (gc == None) if (gc == None)
{ {
XGCValues gcvalues; XGCValues gcvalues;
@@ -856,10 +858,7 @@ public:
// blit results to screen. // blit results to screen.
#if JUCE_USE_XSHM #if JUCE_USE_XSHM
if (isUsingXShm()) if (isUsingXShm())
{
X11Symbols::getInstance()->xShmPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh, True); X11Symbols::getInstance()->xShmPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh, True);
++shmPaintsPendingMap[window];
}
else else
#endif #endif
X11Symbols::getInstance()->xPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh); X11Symbols::getInstance()->xPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh);
@@ -878,7 +877,7 @@ private:
int pixelStride, lineStride; int pixelStride, lineStride;
uint8* imageData = nullptr; uint8* imageData = nullptr;
GC gc = None; GC gc = None;
::Display* display = nullptr;
::Display* display = XWindowSystem::getInstance()->getDisplay();
#if JUCE_USE_XSHM #if JUCE_USE_XSHM
XShmSegmentInfo segmentInfo; XShmSegmentInfo segmentInfo;
@@ -1155,7 +1154,7 @@ namespace ClipboardHelpers
// translate to utf8 // translate to utf8
numDataItems = localContent.getNumBytesAsUTF8() + 1; numDataItems = localContent.getNumBytesAsUTF8() + 1;
data.calloc (numDataItems + 1);
data.calloc (numDataItems);
localContent.copyToUTF8 (data, numDataItems); localContent.copyToUTF8 (data, numDataItems);
propertyFormat = 8; // bits/item propertyFormat = 8; // bits/item
} }
@@ -1165,7 +1164,7 @@ namespace ClipboardHelpers
numDataItems = 2; numDataItems = 2;
propertyFormat = 32; // atoms are 32-bit propertyFormat = 32; // atoms are 32-bit
data.calloc (numDataItems * 4); data.calloc (numDataItems * 4);
Atom* atoms = reinterpret_cast<Atom*> (data.getData());
Atom* atoms = unalignedPointerCast<Atom*> (data.getData());
atoms[0] = XWindowSystem::getInstance()->getAtoms().utf8String; atoms[0] = XWindowSystem::getInstance()->getAtoms().utf8String;
atoms[1] = XA_STRING; atoms[1] = XA_STRING;
@@ -1224,7 +1223,7 @@ ComponentPeer* getPeerFor (::Window windowH)
X11Symbols::getInstance()->xFindContext (display, (XID) windowH, windowHandleXContext, &peer); 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; 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 // Set up the window attributes
XSetWindowAttributes swa; XSetWindowAttributes swa;
swa.border_pixel = 0; swa.border_pixel = 0;
@@ -1313,11 +1319,9 @@ static int getAllEventsMask (bool ignoresMouseClicks)
swa.override_redirect = ((styleFlags & ComponentPeer::windowIsTemporary) != 0) ? True : False; swa.override_redirect = ((styleFlags & ComponentPeer::windowIsTemporary) != 0) ? True : False;
swa.event_mask = getAllEventsMask (styleFlags & ComponentPeer::windowIgnoresMouseClicks); 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, auto windowH = X11Symbols::getInstance()->xCreateWindow (display, parentToAddTo != 0 ? parentToAddTo : root,
0, 0, 1, 1, 0, 0, 1, 1,
0, depth, InputOutput, visual,
0, visualAndDepth.depth, InputOutput, visualAndDepth.visual,
CWBorderPixel | CWColormap | CWBackPixmap | CWEventMask | CWOverrideRedirect, CWBorderPixel | CWColormap | CWBackPixmap | CWEventMask | CWOverrideRedirect,
&swa); &swa);
@@ -1334,12 +1338,28 @@ static int getAllEventsMask (bool ignoresMouseClicks)
} }
// Set window manager hints // 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 // Set the window type
setWindowType (windowH, styleFlags); setWindowType (windowH, styleFlags);
@@ -1407,7 +1427,10 @@ void XWindowSystem::destroyWindow (::Window windowH)
&event) == True) &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) if (wmHints == nullptr)
wmHints = X11Symbols::getInstance()->xAllocWMHints(); 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); X11Symbols::getInstance()->xSync (display, False);
} }
@@ -1514,22 +1540,24 @@ void XWindowSystem::setBounds (::Window windowH, Rectangle<int> newBounds, bool
XWindowSystemUtilities::ScopedXLock xLock; 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(); auto windowBorder = peer->getFrameSize();
@@ -1787,16 +1815,18 @@ bool XWindowSystem::canUseARGBImages() const
return canUseARGB; 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 #if JUCE_USE_XSHM
return Image (new XBitmapImage (display, argb ? Image::ARGB : Image::RGB,
return Image (new XBitmapImage (argb ? Image::ARGB : Image::RGB,
#else #else
return Image (new XBitmapImage (display, Image::RGB,
return Image (new XBitmapImage (Image::RGB,
#endif #endif
(width + 31) & ~31, (width + 31) & ~31,
(height + 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 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()); destinationRect.getX() - totalRect.getX(), destinationRect.getY() - totalRect.getY());
} }
int XWindowSystem::getNumPaintsPending (::Window windowH) const
void XWindowSystem::processPendingPaintsForWindow (::Window windowH)
{ {
#if JUCE_USE_XSHM #if JUCE_USE_XSHM
if (shmPaintsPendingMap[windowH] != 0)
if (! XSHMHelpers::isShmAvailable (display))
return;
if (getNumPaintsPendingForWindow (windowH) > 0)
{ {
XWindowSystemUtilities::ScopedXLock xLock; XWindowSystemUtilities::ScopedXLock xLock;
XEvent evt; XEvent evt;
while (X11Symbols::getInstance()->xCheckTypedWindowEvent (display, windowH, shmCompletionEvent, &evt)) while (X11Symbols::getInstance()->xCheckTypedWindowEvent (display, windowH, shmCompletionEvent, &evt))
--shmPaintsPendingMap[windowH];
removePendingPaintForWindow (windowH);
} }
#endif #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 void XWindowSystem::setScreenSaverEnabled (bool enabled) const
@@ -2107,24 +2164,39 @@ ModifierKeys XWindowSystem::getNativeRealtimeModifiers() const
return ModifierKeys::currentModifiers; 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 #if JUCE_USE_XRANDR
{ {
@@ -2137,11 +2209,13 @@ Array<Displays::Display> XWindowSystem::findDisplays (float masterScale) const
for (int i = 0; i < numMonitors; ++i) 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; 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) for (int j = 0; j < screens->noutput; ++j)
{ {
@@ -2225,25 +2299,22 @@ Array<Displays::Display> XWindowSystem::findDisplays (float masterScale) const
if (displays.isEmpty()) if (displays.isEmpty())
#endif #endif
{ {
if (hints != None)
if (workAreaHints != None)
{ {
auto numMonitors = X11Symbols::getInstance()->xScreenCount (display); auto numMonitors = X11Symbols::getInstance()->xScreenCount (display);
for (int i = 0; i < numMonitors; ++i) 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; 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.isMain = displays.isEmpty();
d.scale = masterScale; d.scale = masterScale;
d.dpi = DisplayHelpers::getDisplayDPI (display, i); d.dpi = DisplayHelpers::getDisplayDPI (display, i);
@@ -2675,6 +2746,40 @@ long XWindowSystem::getUserTime (::Window windowH) const
return result; 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() bool XWindowSystem::initialiseXDisplay()
{ {
@@ -2708,7 +2813,8 @@ bool XWindowSystem::initialiseXDisplay()
// Create our message window (this will never be mapped) // Create our message window (this will never be mapped)
auto screen = X11Symbols::getInstance()->xDefaultScreen (display); 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, 0, 0, 1, 1, 0, 0, InputOnly,
X11Symbols::getInstance()->xDefaultVisual (display, screen), X11Symbols::getInstance()->xDefaultVisual (display, screen),
CWEventMask, &swa); CWEventMask, &swa);
@@ -2717,22 +2823,6 @@ bool XWindowSystem::initialiseXDisplay()
atoms = XWindowSystemUtilities::Atoms (display); 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(); initialisePointerMap();
updateModifierMappings(); updateModifierMappings();
@@ -2741,6 +2831,14 @@ bool XWindowSystem::initialiseXDisplay()
shmCompletionEvent = X11Symbols::getInstance()->xShmGetEventBase (display) + ShmCompletion; shmCompletionEvent = X11Symbols::getInstance()->xShmGetEventBase (display) + ShmCompletion;
#endif #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 // Setup input event handler
LinuxEventLoop::registerFdCallback (X11Symbols::getInstance()->xConnectionNumber (display), LinuxEventLoop::registerFdCallback (X11Symbols::getInstance()->xConnectionNumber (display),
[this] (int) [this] (int)
@@ -2788,10 +2886,10 @@ void XWindowSystem::destroyXDisplay()
X11Symbols::getInstance()->xSync (display, True); X11Symbols::getInstance()->xSync (display, True);
LinuxEventLoop::unregisterFdCallback (X11Symbols::getInstance()->xConnectionNumber (display)); LinuxEventLoop::unregisterFdCallback (X11Symbols::getInstance()->xConnectionNumber (display));
visual = nullptr;
X11Symbols::getInstance()->xCloseDisplay (display); X11Symbols::getInstance()->xCloseDisplay (display);
display = nullptr; display = nullptr;
displayVisuals = nullptr;
} }
} }
@@ -2870,7 +2968,7 @@ void XWindowSystem::handleWindowMessage (LinuxComponentPeer<::Window>* peer, XEv
XWindowSystemUtilities::ScopedXLock xLock; XWindowSystemUtilities::ScopedXLock xLock;
if (event.xany.type == shmCompletionEvent) if (event.xany.type == shmCompletionEvent)
--shmPaintsPendingMap[(::Window) peer->getNativeHandle()];
XWindowSystem::getInstance()->removePendingPaintForWindow ((::Window) peer->getNativeHandle());
} }
#endif #endif
break; break;
@@ -2978,7 +3076,7 @@ void XWindowSystem::handleKeyPressEvent (LinuxComponentPeer<::Window>* peer, XKe
if (sym >= XK_F1 && sym <= XK_F35) if (sym >= XK_F1 && sym <= XK_F35)
{ {
keyPressed = true; keyPressed = true;
keyCode = (sym & 0xff) | Keys::extendedKeyModifier;
keyCode = static_cast<int> ((sym & 0xff) | Keys::extendedKeyModifier);
} }
break; 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 canUseSemiTransparentWindows() const;
bool canUseARGBImages() 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 blitToWindow (::Window windowH, Image image, Rectangle<int> destinationRect, Rectangle<int> totalRect) const;
void setScreenSaverEnabled (bool enabled) const; void setScreenSaverEnabled (bool enabled) const;
@@ -171,6 +174,24 @@ private:
~XWindowSystem(); ~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(); bool initialiseXDisplay();
void destroyXDisplay(); void destroyXDisplay();
@@ -217,10 +238,13 @@ private:
XWindowSystemUtilities::Atoms atoms; XWindowSystemUtilities::Atoms atoms;
::Display* display = nullptr; ::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] = {}; int pointerMap[5] = {};
String localClipboardContent; 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) button->setColour (ToggleButton::tickColourId, button->getLookAndFeel().findColour (ToggleButton::tickColourId)
.withAlpha (usingDefault ? 0.4f : 1.0f)); .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) Iterator (const TextEditor& ed)
: sections (ed.sections), : sections (ed.sections),
justification (ed.justification), justification (ed.justification),
justificationWidth (ed.getJustificationWidth()),
bottomRight (ed.getMaximumWidth(), ed.getMaximumHeight()),
wordWrapWidth (ed.getWordWrapWidth()), wordWrapWidth (ed.getWordWrapWidth()),
passwordCharacter (ed.passwordCharacter), passwordCharacter (ed.passwordCharacter),
lineSpacing (ed.lineSpacing)
lineSpacing (ed.lineSpacing),
underlineWhitespace (ed.underlineWhitespace)
{ {
jassert (wordWrapWidth > 0); jassert (wordWrapWidth > 0);
@@ -292,6 +293,8 @@ struct TextEditor::Iterator
if (currentSection != nullptr) if (currentSection != nullptr)
beginNewLine(); beginNewLine();
} }
lineHeight = ed.currentFont.getHeight();
} }
Iterator (const Iterator&) = default; Iterator (const Iterator&) = default;
@@ -325,7 +328,7 @@ struct TextEditor::Iterator
{ {
tempAtom.numChars = (uint16) split; tempAtom.numChars = (uint16) split;
tempAtom.width = g.getGlyph (split - 1).getRight(); tempAtom.width = g.getGlyph (split - 1).getRight();
atomX = getJustificationOffset (tempAtom.width);
atomX = getJustificationOffsetX (tempAtom.width);
atomRight = atomX + tempAtom.width; atomRight = atomX + tempAtom.width;
return true; return true;
} }
@@ -427,14 +430,14 @@ struct TextEditor::Iterator
tempAtom.numChars = 0; tempAtom.numChars = 0;
atom = &tempAtom; atom = &tempAtom;
if (atomX > justificationOffset)
if (atomX > justificationOffsetX)
beginNewLine(); beginNewLine();
return next(); return next();
} }
beginNewLine(); beginNewLine();
atomX = justificationOffset;
atomX = justificationOffsetX;
atomRight = atomX + atom->width; atomRight = atomX + atom->width;
return true; return true;
} }
@@ -494,25 +497,22 @@ struct TextEditor::Iterator
++tempAtomIndex; ++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; 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) if (lastSection != currentSection)
{ {
@@ -527,7 +527,7 @@ struct TextEditor::Iterator
ga.addLineOfText (currentSection->font, ga.addLineOfText (currentSection->font,
atom->getTrimmedText (passwordCharacter), atom->getTrimmedText (passwordCharacter),
atomX, (float) roundToInt (lineY + lineHeight - maxDescent)); 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); 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 startX = roundToInt (indexToX (underline.getStart()));
auto endX = roundToInt (indexToX (underline.getEnd())); auto endX = roundToInt (indexToX (underline.getEnd()));
auto baselineY = roundToInt (lineY + currentSection->font.getAscent() + 0.5f); auto baselineY = roundToInt (lineY + currentSection->font.getAscent() + 0.5f);
Graphics::ScopedSaveState state (g); Graphics::ScopedSaveState state (g);
g.addTransform (transform);
g.reduceClipRegion ({ startX, baselineY, endX - startX, 1 }); g.reduceClipRegion ({ startX, baselineY, endX - startX, 1 });
g.fillCheckerBoard ({ (float) endX, (float) baselineY + 1.0f }, 3.0f, 1.0f, colour, Colours::transparentBlack); 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()) if (passwordCharacter != 0 || ! atom->isWhitespace())
{ {
@@ -566,7 +567,7 @@ struct TextEditor::Iterator
ga.removeRangeOfGlyphs (selected.getEnd() - indexInText, -1); ga.removeRangeOfGlyphs (selected.getEnd() - indexInText, -1);
g.setColour (currentSection->colour); g.setColour (currentSection->colour);
ga2.draw (g);
ga2.draw (g, transform);
} }
if (selected.getStart() > indexInText) if (selected.getStart() > indexInText)
@@ -576,11 +577,11 @@ struct TextEditor::Iterator
ga.removeRangeOfGlyphs (0, selected.getStart() - indexInText); ga.removeRangeOfGlyphs (0, selected.getStart() - indexInText);
g.setColour (currentSection->colour); g.setColour (currentSection->colour);
ga2.draw (g);
ga2.draw (g, transform);
} }
g.setColour (selectedTextColour); g.setColour (selectedTextColour);
ga.draw (g);
ga.draw (g, transform);
} }
} }
@@ -649,20 +650,42 @@ struct TextEditor::Iterator
return false; 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; int indexInText = 0;
float lineY = 0, justificationOffset = 0, lineHeight = 0, maxDescent = 0;
float lineY = 0, lineHeight = 0, maxDescent = 0;
float atomX = 0, atomRight = 0; float atomX = 0, atomRight = 0;
const TextAtom* atom = nullptr; const TextAtom* atom = nullptr;
const UniformTextSection* currentSection = nullptr;
private: private:
const OwnedArray<UniformTextSection>& sections; const OwnedArray<UniformTextSection>& sections;
const UniformTextSection* currentSection = nullptr;
int sectionIndex = 0, atomIndex = 0; int sectionIndex = 0, atomIndex = 0;
Justification justification; Justification justification;
const float justificationWidth, wordWrapWidth;
float justificationOffsetX = 0;
const Point<float> bottomRight;
const float wordWrapWidth;
const juce_wchar passwordCharacter; const juce_wchar passwordCharacter;
const float lineSpacing; const float lineSpacing;
const bool underlineWhitespace;
TextAtom tempAtom; TextAtom tempAtom;
void moveToEndOfLastAtom() void moveToEndOfLastAtom()
@@ -673,7 +696,7 @@ private:
if (atom->isNewLine()) if (atom->isNewLine())
{ {
atomX = 0.0f;
atomX = getJustificationOffsetX (0);
lineY += lineHeight * lineSpacing; lineY += lineHeight * lineSpacing;
} }
} }
@@ -826,8 +849,8 @@ struct TextEditor::TextEditorViewport : public Viewport
void visibleAreaChanged (const Rectangle<int>&) override 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(); auto wordWrapWidth = owner.getWordWrapWidth();
@@ -835,9 +858,8 @@ struct TextEditor::TextEditorViewport : public Viewport
{ {
lastWordWrapWidth = wordWrapWidth; 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: private:
TextEditor& owner; TextEditor& owner;
float lastWordWrapWidth = 0; float lastWordWrapWidth = 0;
bool rentrant = false;
bool reentrant = false;
JUCE_DECLARE_NON_COPYABLE (TextEditorViewport) JUCE_DECLARE_NON_COPYABLE (TextEditorViewport)
}; };
@@ -936,8 +958,8 @@ void TextEditor::setMultiLine (const bool shouldBeMultiLine,
multiline = shouldBeMultiLine; multiline = shouldBeMultiLine;
wordWrap = shouldWordWrap && shouldBeMultiLine; wordWrap = shouldWordWrap && shouldBeMultiLine;
viewport->setScrollBarsShown (scrollbarVisible && multiline,
scrollbarVisible && multiline);
checkLayout();
viewport->setViewPosition (0, 0); viewport->setViewPosition (0, 0);
resized(); resized();
scrollToMakeSureCursorIsVisible(); scrollToMakeSureCursorIsVisible();
@@ -954,8 +976,7 @@ void TextEditor::setScrollbarsShown (bool shown)
if (scrollbarVisible != shown) if (scrollbarVisible != shown)
{ {
scrollbarVisible = shown; scrollbarVisible = shown;
shown = shown && isMultiLine();
viewport->setScrollBarsShown (shown, shown);
checkLayout();
} }
} }
@@ -1003,7 +1024,9 @@ void TextEditor::setJustification (Justification j)
if (justification != j) if (justification != j)
{ {
justification = j; justification = j;
resized(); resized();
repaint();
} }
} }
@@ -1028,7 +1051,7 @@ void TextEditor::applyFontToAllText (const Font& newFont, bool changeCurrentFont
} }
coalesceSimilarSections(); coalesceSimilarSections();
updateTextHolderSize();
checkLayout();
scrollToMakeSureCursorIsVisible(); scrollToMakeSureCursorIsVisible();
repaint(); repaint();
} }
@@ -1090,8 +1113,13 @@ void TextEditor::recreateCaret()
void TextEditor::updateCaretPosition() 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) TextEditor::LengthAndCharacterRestriction::LengthAndCharacterRestriction (int maxLen, const String& chars)
@@ -1146,7 +1174,7 @@ void TextEditor::setScrollBarThickness (int newThicknessPixels)
void TextEditor::clear() void TextEditor::clear()
{ {
clearInternal (nullptr); clearInternal (nullptr);
updateTextHolderSize();
checkLayout();
undoManager.clearUndoHistory(); undoManager.clearUndoHistory();
} }
@@ -1181,7 +1209,7 @@ void TextEditor::setText (const String& newText, bool sendTextChangeMessage)
else else
textValue.addListener (textHolder); textValue.addListener (textHolder);
updateTextHolderSize();
checkLayout();
scrollToMakeSureCursorIsVisible(); scrollToMakeSureCursorIsVisible();
undoManager.clearUndoHistory(); undoManager.clearUndoHistory();
@@ -1214,7 +1242,7 @@ void TextEditor::textWasChangedByValue()
//============================================================================== //==============================================================================
void TextEditor::textChanged() void TextEditor::textChanged()
{ {
updateTextHolderSize();
checkLayout();
if (listeners.size() != 0 || onTextChange != nullptr) if (listeners.size() != 0 || onTextChange != nullptr)
postCommandMessage (TextEditorDefs::textChangeMessageId); postCommandMessage (TextEditorDefs::textChangeMessageId);
@@ -1259,30 +1287,33 @@ void TextEditor::repaintText (Range<int> range)
{ {
if (! range.isEmpty()) 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 float TextEditor::getWordWrapWidth() const
{ {
return wordWrap ? getJustificationWidth()
return wordWrap ? getMaximumWidth()
: std::numeric_limits<float>::max(); : std::numeric_limits<float>::max();
} }
float TextEditor::getJustificationWidth() const
float TextEditor::getMaximumWidth() const
{ {
return (float) (viewport->getMaximumVisibleWidth() - (leftIndent + rightEdgeSpace + 1)); 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) if (getWordWrapWidth() > 0)
{ {
float maxWidth = getJustificationWidth();
auto maxWidth = getMaximumWidth();
Iterator i (*this); Iterator i (*this);
while (i.next()) while (i.next())
maxWidth = jmax (maxWidth, i.atomRight); 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) 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) 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) int TextEditor::getTextIndexAt (const int x, const int y)
{ {
Iterator i (*this);
return indexAtPosition ((float) (x + viewport->getViewPositionX() - leftIndent - borderSize.getLeft()), 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) void TextEditor::insertTextAtCaret (const String& t)
@@ -1576,8 +1648,19 @@ void TextEditor::drawContent (Graphics& g)
{ {
g.setOrigin (leftIndent, topIndent); g.setOrigin (leftIndent, topIndent);
auto clip = g.getClipBounds(); 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); Iterator i (*this);
Colour selectedTextColour;
if (! selection.isEmpty()) 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); selectedTextColour = findColour (highlightedTextColourId);
g.setColour (findColour (highlightColourId).withMultipliedAlpha (hasKeyboardFocus (true) ? 1.0f : 0.5f));
g.fillPath (selectionArea.toPath(), transform);
} }
const UniformTextSection* lastSection = nullptr; const UniformTextSection* lastSection = nullptr;
@@ -1607,12 +1690,12 @@ void TextEditor::drawContent (Graphics& g)
{ {
if (selection.intersects ({ i.indexInText, i.indexInText + i.atom->numChars })) if (selection.intersects ({ i.indexInText, i.indexInText + i.atom->numChars }))
{ {
i.drawSelectedText (g, selection, selectedTextColour);
i.drawSelectedText (g, selection, selectedTextColour, transform);
lastSection = nullptr; lastSection = nullptr;
} }
else 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() if (i2.lineY + i2.lineHeight >= (float) clip.getY()
&& underlinedSection.intersects ({ i2.indexInText, i2.indexInText + i2.atom->numChars })) && 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.setColour (colourForTextWhenEmpty);
g.setFont (getFont()); 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); 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(); newTransaction();
@@ -2067,6 +2147,9 @@ void TextEditor::focusGained (FocusChangeType)
checkFocus(); checkFocus();
if (cause == FocusChangeType::focusChangedByMouseClick && selectAllTextWhenFocused)
wasFocused = false;
repaint(); repaint();
updateCaretPosition(); updateCaretPosition();
} }
@@ -2095,7 +2178,7 @@ void TextEditor::resized()
viewport->setBoundsInset (borderSize); viewport->setBoundsInset (borderSize);
viewport->setSingleStepSizes (16, roundToInt (currentFont.getHeight())); viewport->setSingleStepSizes (16, roundToInt (currentFont.getHeight()));
updateTextHolderSize();
checkLayout();
if (isMultiLine()) if (isMultiLine())
updateCaretPosition(); updateCaretPosition();
@@ -2213,7 +2296,7 @@ void TextEditor::insert (const String& text, int insertIndex, const Font& font,
totalNumChars = -1; totalNumChars = -1;
valueTextNeedsUpdating = true; valueTextNeedsUpdating = true;
updateTextHolderSize();
checkLayout();
moveCaretTo (caretPositionToMoveTo, false); moveCaretTo (caretPositionToMoveTo, false);
repaintText ({ insertIndex, getTotalNumChars() }); repaintText ({ insertIndex, getTotalNumChars() });
@@ -2426,7 +2509,7 @@ void TextEditor::getCharPosition (int index, Point<float>& anchor, float& lineHe
if (sections.isEmpty()) if (sections.isEmpty())
{ {
anchor = { i.getJustificationOffset (0), 0 };
anchor = { i.getJustificationOffsetX (0), 0 };
lineHeight = currentFont.getHeight(); lineHeight = currentFont.getHeight();
} }
else 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; } bool areScrollbarsShown() const noexcept { return scrollbarVisible; }
/** Changes the password character used to disguise the text. /** Changes the password character used to disguise the text.
@param passwordCharacter if this is not zero, this character will be used as a replacement @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; } juce_wchar getPasswordCharacter() const noexcept { return passwordCharacter; }
//============================================================================== //==============================================================================
/** Allows a right-click menu to appear for the editor. /** Allows a right-click menu to appear for the editor.
@@ -251,7 +249,7 @@ public:
@see setFont @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. /** Applies a colour to all the text in the editor.
@@ -260,6 +258,18 @@ public:
*/ */
void applyColourToAllText (const Colour& newColour, bool changeCurrentTextColour = true); 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. /** If set to true, focusing on the editor will highlight all its text.
@@ -495,9 +505,12 @@ public:
*/ */
void setScrollToShowCursor (bool shouldScrollToShowCaret); 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); 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. /** 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 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. 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; std::unique_ptr<Viewport> viewport;
TextHolderComponent* textHolder; TextHolderComponent* textHolder;
BorderSize<int> borderSize { 1, 1, 1, 3 }; BorderSize<int> borderSize { 1, 1, 1, 3 };
Justification justification { Justification::left };
Justification justification { Justification::topLeft };
bool readOnly = false; bool readOnly = false;
bool caretVisible = true; bool caretVisible = true;
@@ -728,6 +741,7 @@ private:
bool menuActive = false; bool menuActive = false;
bool valueTextNeedsUpdating = false; bool valueTextNeedsUpdating = false;
bool consumeEscAndReturnKeys = true; bool consumeEscAndReturnKeys = true;
bool underlineWhitespace = true;
UndoManager undoManager; UndoManager undoManager;
std::unique_ptr<CaretComponent> caret; std::unique_ptr<CaretComponent> caret;
@@ -778,9 +792,12 @@ private:
int findWordBreakBefore (int position) const; int findWordBreakBefore (int position) const;
bool moveCaretWithTransaction (int newPos, bool selecting); bool moveCaretWithTransaction (int newPos, bool selecting);
void drawContent (Graphics&); void drawContent (Graphics&);
void updateTextHolderSize();
void checkLayout();
void updateTextHolderSize (int, int);
void updateScrollbarVisibility (int, int);
float getWordWrapWidth() const; float getWordWrapWidth() const;
float getJustificationWidth() const;
float getMaximumWidth() const;
float getMaximumHeight() const;
void timerCallbackInt(); void timerCallbackInt();
void checkFocus(); void checkFocus();
void repaintText (Range<int>); 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 else
{ {
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows()); setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
updatePosition (area, Desktop::getInstance().getDisplays().findDisplayForRect (area).userArea); updatePosition (area, Desktop::getInstance().getDisplays().findDisplayForRect (area).userArea);
addToDesktop (ComponentPeer::windowIsTemporary); addToDesktop (ComponentPeer::windowIsTemporary);
startTimer (100); startTimer (100);
@@ -51,15 +49,14 @@ CallOutBox::CallOutBox (Component& c, Rectangle<int> area, Component* const pare
creationTime = Time::getCurrentTime(); creationTime = Time::getCurrentTime();
} }
CallOutBox::~CallOutBox() = default;
//============================================================================== //==============================================================================
class CallOutBoxCallback : public ModalComponentManager::Callback, class CallOutBoxCallback : public ModalComponentManager::Callback,
private Timer private Timer
{ {
public: 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.setVisible (true);
callout.enterModalState (true, this); callout.enterModalState (true, this);
@@ -80,11 +77,11 @@ public:
JUCE_DECLARE_NON_COPYABLE (CallOutBoxCallback) 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! 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); return jmax (getLookAndFeel().getCallOutBoxBorderSize (*this), (int) arrowSize);
} }
void CallOutBox::lookAndFeelChanged() { resized(); repaint(); }
void CallOutBox::lookAndFeelChanged()
{
resized();
repaint();
}
void CallOutBox::paint (Graphics& g) void CallOutBox::paint (Graphics& g)
{ {
@@ -158,7 +159,7 @@ void CallOutBox::setDismissalMouseClicksAreAlwaysConsumed (bool b) noexcept
dismissalMouseClicksAreAlwaysConsumed = b; dismissalMouseClicksAreAlwaysConsumed = b;
} }
enum { callOutBoxDismissCommandId = 0x4f83a04b };
static constexpr int callOutBoxDismissCommandId = 0x4f83a04b;
void CallOutBox::handleCommandMessage (int commandId) void CallOutBox::handleCommandMessage (int commandId)
{ {
@@ -193,9 +194,8 @@ void CallOutBox::updatePosition (const Rectangle<int>& newAreaToPointTo, const R
availableArea = newAreaToFitIn; availableArea = newAreaToFitIn;
auto borderSpace = getBorderSize(); 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 hw = newBounds.getWidth() / 2;
auto hh = newBounds.getHeight() / 2; auto hh = newBounds.getHeight() / 2;
@@ -250,7 +250,7 @@ void CallOutBox::refreshPath()
const float gap = 4.5f; 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(), getLocalBounds().toFloat(),
targetPoint - getPosition().toFloat(), targetPoint - getPosition().toFloat(),
getLookAndFeel().getCallOutBoxCornerSize (*this), arrowSize * 0.7f); 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 @code
void mouseUp (const MouseEvent&) void mouseUp (const MouseEvent&)
{ {
FoobarContentComp* content = new FoobarContentComp();
auto content = std::make_unique<FoobarContentComp>();
content->setSize (300, 300); content->setSize (300, 300);
CallOutBox& myBox
= CallOutBox::launchAsynchronously (content, getScreenBounds(), nullptr);
auto& myBox = CallOutBox::launchAsynchronously (std::move (content),
getScreenBounds(),
nullptr);
} }
@endcode @endcode
@@ -77,9 +78,6 @@ public:
Rectangle<int> areaToPointTo, Rectangle<int> areaToPointTo,
Component* parentComponent); Component* parentComponent);
/** Destructor. */
~CallOutBox() override;
//============================================================================== //==============================================================================
/** Changes the base width of the arrow. */ /** Changes the base width of the arrow. */
void setArrowSize (float newSize); void setArrowSize (float newSize);
@@ -109,15 +107,13 @@ public:
@param contentComponent the component to display inside the call-out. This should @param contentComponent the component to display inside the call-out. This should
already have a size set (although the call-out will also already have a size set (although the call-out will also
update itself when the component's size is changed later). 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 @param areaToPointTo the area that the call-out's arrow should point towards. If
a parentComponent is supplied, then this is relative to that a parentComponent is supplied, then this is relative to that
parent; otherwise, it's a global screen coord. parent; otherwise, it's a global screen coord.
@param parentComponent if not a nullptr, this is the component to add the call-out to. @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. 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, Rectangle<int> areaToPointTo,
Component* parentComponent); 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())); 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 Rectangle<int> ComponentPeer::getAreaCoveredBy (Component& subComponent) const
{ {
return ScalingHelpers::scaledScreenPosToUnscaled return ScalingHelpers::scaledScreenPosToUnscaled


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

Loading…
Cancel
Save