| @@ -2,8 +2,8 @@ | |||||
| set -e | set -e | ||||
| JUCE_MODULES_DIR="/home/falktx/Personal/FOSS/GIT/distrho/DISTRHO/libs/juce/source/modules/" | |||||
| CARLA_MODULES_DIR="/home/falktx/Personal/FOSS/GIT/falktx/Carla/source/modules/" | |||||
| JUCE_MODULES_DIR="/home/falktx/FOSS/GIT-mine/DISTRHO/libs/juce/source/modules/" | |||||
| CARLA_MODULES_DIR="/home/falktx/FOSS/GIT-mine/Carla/source/modules/" | |||||
| MODULES=("juce_audio_basics juce_audio_devices juce_audio_formats juce_audio_processors juce_core juce_data_structures juce_events juce_graphics juce_gui_basics juce_gui_extra") | MODULES=("juce_audio_basics juce_audio_devices juce_audio_formats juce_audio_processors juce_core juce_data_structures juce_events juce_graphics juce_gui_basics juce_gui_extra") | ||||
| @@ -155,7 +155,7 @@ MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp) | |||||
| MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte, double t) | MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte, double t) | ||||
| : timeStamp (t) | : timeStamp (t) | ||||
| { | { | ||||
| const uint8* src = static_cast <const uint8*> (srcData); | |||||
| const uint8* src = static_cast<const uint8*> (srcData); | |||||
| unsigned int byte = (unsigned int) *src; | unsigned int byte = (unsigned int) *src; | ||||
| if (byte < 0x80) | if (byte < 0x80) | ||||
| @@ -658,6 +658,36 @@ String MidiMessage::getTextFromTextMetaEvent() const | |||||
| CharPointer_UTF8 (textData + getMetaEventLength())); | CharPointer_UTF8 (textData + getMetaEventLength())); | ||||
| } | } | ||||
| MidiMessage MidiMessage::textMetaEvent (int type, StringRef text) | |||||
| { | |||||
| jassert (type > 0 && type < 16) | |||||
| MidiMessage result; | |||||
| const size_t textSize = text.text.sizeInBytes() - 1; | |||||
| uint8 header[8]; | |||||
| size_t n = sizeof (header); | |||||
| header[--n] = (uint8) (textSize & 0x7f); | |||||
| for (size_t i = textSize; (i >>= 7) != 0;) | |||||
| header[--n] = (uint8) ((i & 0x7f) | 0x80); | |||||
| header[--n] = (uint8) type; | |||||
| header[--n] = 0xff; | |||||
| const size_t headerLen = sizeof (header) - n; | |||||
| uint8* const dest = result.allocateSpace ((int) (headerLen + textSize)); | |||||
| result.size = (int) (headerLen + textSize); | |||||
| memcpy (dest, header + n, headerLen); | |||||
| memcpy (dest + headerLen, text.text.getAddress(), textSize); | |||||
| return result; | |||||
| } | |||||
| bool MidiMessage::isTrackNameEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 3) && (*data == 0xff); } | bool MidiMessage::isTrackNameEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 3) && (*data == 0xff); } | ||||
| bool MidiMessage::isTempoMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 81) && (*data == 0xff); } | bool MidiMessage::isTempoMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 81) && (*data == 0xff); } | ||||
| bool MidiMessage::isMidiChannelMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); } | bool MidiMessage::isMidiChannelMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); } | ||||
| @@ -168,9 +168,7 @@ public: | |||||
| bool isForChannel (int channelNumber) const noexcept; | bool isForChannel (int channelNumber) const noexcept; | ||||
| /** Changes the message's midi channel. | /** Changes the message's midi channel. | ||||
| This won't do anything for non-channel messages like sysexes. | This won't do anything for non-channel messages like sysexes. | ||||
| @param newChannelNumber the channel number to change it to, in the range 1 to 16 | @param newChannelNumber the channel number to change it to, in the range 1 to 16 | ||||
| */ | */ | ||||
| void setChannel (int newChannelNumber) noexcept; | void setChannel (int newChannelNumber) noexcept; | ||||
| @@ -181,17 +179,13 @@ public: | |||||
| bool isSysEx() const noexcept; | bool isSysEx() const noexcept; | ||||
| /** Returns a pointer to the sysex data inside the message. | /** Returns a pointer to the sysex data inside the message. | ||||
| If this event isn't a sysex event, it'll return 0. | If this event isn't a sysex event, it'll return 0. | ||||
| @see getSysExDataSize | @see getSysExDataSize | ||||
| */ | */ | ||||
| const uint8* getSysExData() const noexcept; | const uint8* getSysExData() const noexcept; | ||||
| /** Returns the size of the sysex data. | /** Returns the size of the sysex data. | ||||
| This value excludes the 0xf0 header byte and the 0xf7 at the end. | This value excludes the 0xf0 header byte and the 0xf7 at the end. | ||||
| @see getSysExData | @see getSysExData | ||||
| */ | */ | ||||
| int getSysExDataSize() const noexcept; | int getSysExDataSize() const noexcept; | ||||
| @@ -252,15 +246,12 @@ public: | |||||
| bool isNoteOnOrOff() const noexcept; | bool isNoteOnOrOff() const noexcept; | ||||
| /** Returns the midi note number for note-on and note-off messages. | /** Returns the midi note number for note-on and note-off messages. | ||||
| If the message isn't a note-on or off, the value returned is undefined. | If the message isn't a note-on or off, the value returned is undefined. | ||||
| @see isNoteOff, getMidiNoteName, getMidiNoteInHertz, setNoteNumber | @see isNoteOff, getMidiNoteName, getMidiNoteInHertz, setNoteNumber | ||||
| */ | */ | ||||
| int getNoteNumber() const noexcept; | int getNoteNumber() const noexcept; | ||||
| /** Changes the midi note number of a note-on or note-off message. | /** Changes the midi note number of a note-on or note-off message. | ||||
| If the message isn't a note on or off, this will do nothing. | If the message isn't a note on or off, this will do nothing. | ||||
| */ | */ | ||||
| void setNoteNumber (int newNoteNumber) noexcept; | void setNoteNumber (int newNoteNumber) noexcept; | ||||
| @@ -320,16 +311,12 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns true if the message is a program (patch) change message. | /** Returns true if the message is a program (patch) change message. | ||||
| @see getProgramChangeNumber, getGMInstrumentName | @see getProgramChangeNumber, getGMInstrumentName | ||||
| */ | */ | ||||
| bool isProgramChange() const noexcept; | bool isProgramChange() const noexcept; | ||||
| /** Returns the new program number of a program change message. | /** Returns the new program number of a program change message. | ||||
| If the message isn't a program change, the value returned will be | |||||
| nonsense. | |||||
| If the message isn't a program change, the value returned is undefined. | |||||
| @see isProgramChange, getGMInstrumentName | @see isProgramChange, getGMInstrumentName | ||||
| */ | */ | ||||
| int getProgramChangeNumber() const noexcept; | int getProgramChangeNumber() const noexcept; | ||||
| @@ -344,7 +331,6 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns true if the message is a pitch-wheel move. | /** Returns true if the message is a pitch-wheel move. | ||||
| @see getPitchWheelValue, pitchWheel | @see getPitchWheelValue, pitchWheel | ||||
| */ | */ | ||||
| bool isPitchWheel() const noexcept; | bool isPitchWheel() const noexcept; | ||||
| @@ -433,7 +419,6 @@ public: | |||||
| /** Returns the controller number of a controller message. | /** Returns the controller number of a controller message. | ||||
| The name of the controller can be looked up using the getControllerName() method. | The name of the controller can be looked up using the getControllerName() method. | ||||
| Note that the value returned is invalid for messages that aren't controller changes. | Note that the value returned is invalid for messages that aren't controller changes. | ||||
| @see isController, getControllerName, getControllerValue | @see isController, getControllerName, getControllerValue | ||||
| @@ -443,7 +428,6 @@ public: | |||||
| /** Returns the controller value from a controller message. | /** Returns the controller value from a controller message. | ||||
| A value 0 to 127 is returned to indicate the new controller position. | A value 0 to 127 is returned to indicate the new controller position. | ||||
| Note that the value returned is invalid for messages that aren't controller changes. | Note that the value returned is invalid for messages that aren't controller changes. | ||||
| @see isController, getControllerNumber | @see isController, getControllerNumber | ||||
| @@ -467,13 +451,11 @@ public: | |||||
| int value) noexcept; | int value) noexcept; | ||||
| /** Checks whether this message is an all-notes-off message. | /** Checks whether this message is an all-notes-off message. | ||||
| @see allNotesOff | @see allNotesOff | ||||
| */ | */ | ||||
| bool isAllNotesOff() const noexcept; | bool isAllNotesOff() const noexcept; | ||||
| /** Checks whether this message is an all-sound-off message. | /** Checks whether this message is an all-sound-off message. | ||||
| @see allSoundOff | @see allSoundOff | ||||
| */ | */ | ||||
| bool isAllSoundOff() const noexcept; | bool isAllSoundOff() const noexcept; | ||||
| @@ -520,13 +502,11 @@ public: | |||||
| int getMetaEventType() const noexcept; | int getMetaEventType() const noexcept; | ||||
| /** Returns a pointer to the data in a meta-event. | /** Returns a pointer to the data in a meta-event. | ||||
| @see isMetaEvent, getMetaEventLength | @see isMetaEvent, getMetaEventLength | ||||
| */ | */ | ||||
| const uint8* getMetaEventData() const noexcept; | const uint8* getMetaEventData() const noexcept; | ||||
| /** Returns the length of the data for a meta-event. | /** Returns the length of the data for a meta-event. | ||||
| @see isMetaEvent, getMetaEventData | @see isMetaEvent, getMetaEventData | ||||
| */ | */ | ||||
| int getMetaEventLength() const noexcept; | int getMetaEventLength() const noexcept; | ||||
| @@ -539,29 +519,28 @@ public: | |||||
| bool isEndOfTrackMetaEvent() const noexcept; | bool isEndOfTrackMetaEvent() const noexcept; | ||||
| /** Creates an end-of-track meta-event. | /** Creates an end-of-track meta-event. | ||||
| @see isEndOfTrackMetaEvent | @see isEndOfTrackMetaEvent | ||||
| */ | */ | ||||
| static MidiMessage endOfTrack() noexcept; | static MidiMessage endOfTrack() noexcept; | ||||
| /** Returns true if this is an 'track name' meta-event. | /** Returns true if this is an 'track name' meta-event. | ||||
| You can use the getTextFromTextMetaEvent() method to get the track's name. | You can use the getTextFromTextMetaEvent() method to get the track's name. | ||||
| */ | */ | ||||
| bool isTrackNameEvent() const noexcept; | bool isTrackNameEvent() const noexcept; | ||||
| /** Returns true if this is a 'text' meta-event. | /** Returns true if this is a 'text' meta-event. | ||||
| @see getTextFromTextMetaEvent | @see getTextFromTextMetaEvent | ||||
| */ | */ | ||||
| bool isTextMetaEvent() const noexcept; | bool isTextMetaEvent() const noexcept; | ||||
| /** Returns the text from a text meta-event. | /** Returns the text from a text meta-event. | ||||
| @see isTextMetaEvent | @see isTextMetaEvent | ||||
| */ | */ | ||||
| String getTextFromTextMetaEvent() const; | String getTextFromTextMetaEvent() const; | ||||
| /** Creates a text meta-event. */ | |||||
| static MidiMessage textMetaEvent (int type, StringRef text); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns true if this is a 'tempo' meta-event. | /** Returns true if this is a 'tempo' meta-event. | ||||
| @see getTempoMetaEventTickLength, getTempoSecondsPerQuarterNote | @see getTempoMetaEventTickLength, getTempoSecondsPerQuarterNote | ||||
| @@ -660,7 +639,6 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns true if this is a midi start event. | /** Returns true if this is a midi start event. | ||||
| @see midiStart | @see midiStart | ||||
| */ | */ | ||||
| bool isMidiStart() const noexcept; | bool isMidiStart() const noexcept; | ||||
| @@ -669,7 +647,6 @@ public: | |||||
| static MidiMessage midiStart() noexcept; | static MidiMessage midiStart() noexcept; | ||||
| /** Returns true if this is a midi continue event. | /** Returns true if this is a midi continue event. | ||||
| @see midiContinue | @see midiContinue | ||||
| */ | */ | ||||
| bool isMidiContinue() const noexcept; | bool isMidiContinue() const noexcept; | ||||
| @@ -678,7 +655,6 @@ public: | |||||
| static MidiMessage midiContinue() noexcept; | static MidiMessage midiContinue() noexcept; | ||||
| /** Returns true if this is a midi stop event. | /** Returns true if this is a midi stop event. | ||||
| @see midiStop | @see midiStop | ||||
| */ | */ | ||||
| bool isMidiStop() const noexcept; | bool isMidiStop() const noexcept; | ||||
| @@ -687,7 +663,6 @@ public: | |||||
| static MidiMessage midiStop() noexcept; | static MidiMessage midiStop() noexcept; | ||||
| /** Returns true if this is a midi clock event. | /** Returns true if this is a midi clock event. | ||||
| @see midiClock, songPositionPointer | @see midiClock, songPositionPointer | ||||
| */ | */ | ||||
| bool isMidiClock() const noexcept; | bool isMidiClock() const noexcept; | ||||
| @@ -696,13 +671,11 @@ public: | |||||
| static MidiMessage midiClock() noexcept; | static MidiMessage midiClock() noexcept; | ||||
| /** Returns true if this is a song-position-pointer message. | /** Returns true if this is a song-position-pointer message. | ||||
| @see getSongPositionPointerMidiBeat, songPositionPointer | @see getSongPositionPointerMidiBeat, songPositionPointer | ||||
| */ | */ | ||||
| bool isSongPositionPointer() const noexcept; | bool isSongPositionPointer() const noexcept; | ||||
| /** Returns the midi beat-number of a song-position-pointer message. | /** Returns the midi beat-number of a song-position-pointer message. | ||||
| @see isSongPositionPointer, songPositionPointer | @see isSongPositionPointer, songPositionPointer | ||||
| */ | */ | ||||
| int getSongPositionPointerMidiBeat() const noexcept; | int getSongPositionPointerMidiBeat() const noexcept; | ||||
| @@ -719,23 +692,18 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns true if this is a quarter-frame midi timecode message. | /** Returns true if this is a quarter-frame midi timecode message. | ||||
| @see quarterFrame, getQuarterFrameSequenceNumber, getQuarterFrameValue | @see quarterFrame, getQuarterFrameSequenceNumber, getQuarterFrameValue | ||||
| */ | */ | ||||
| bool isQuarterFrame() const noexcept; | bool isQuarterFrame() const noexcept; | ||||
| /** Returns the sequence number of a quarter-frame midi timecode message. | /** Returns the sequence number of a quarter-frame midi timecode message. | ||||
| This will be a value between 0 and 7. | This will be a value between 0 and 7. | ||||
| @see isQuarterFrame, getQuarterFrameValue, quarterFrame | @see isQuarterFrame, getQuarterFrameValue, quarterFrame | ||||
| */ | */ | ||||
| int getQuarterFrameSequenceNumber() const noexcept; | int getQuarterFrameSequenceNumber() const noexcept; | ||||
| /** Returns the value from a quarter-frame message. | /** Returns the value from a quarter-frame message. | ||||
| This will be the lower nybble of the message's data-byte, a value | |||||
| between 0 and 15 | |||||
| This will be the lower nybble of the message's data-byte, a value between 0 and 15 | |||||
| */ | */ | ||||
| int getQuarterFrameValue() const noexcept; | int getQuarterFrameValue() const noexcept; | ||||
| @@ -747,7 +715,6 @@ public: | |||||
| static MidiMessage quarterFrame (int sequenceNumber, int value) noexcept; | static MidiMessage quarterFrame (int sequenceNumber, int value) noexcept; | ||||
| /** SMPTE timecode types. | /** SMPTE timecode types. | ||||
| Used by the getFullFrameParameters() and fullFrame() methods. | Used by the getFullFrameParameters() and fullFrame() methods. | ||||
| */ | */ | ||||
| enum SmpteTimecodeType | enum SmpteTimecodeType | ||||
| @@ -758,8 +725,7 @@ public: | |||||
| fps30 = 3 | fps30 = 3 | ||||
| }; | }; | ||||
| /** Returns true if this is a full-frame midi timecode message. | |||||
| */ | |||||
| /** Returns true if this is a full-frame midi timecode message. */ | |||||
| bool isFullFrame() const noexcept; | bool isFullFrame() const noexcept; | ||||
| /** Extracts the timecode information from a full-frame midi timecode message. | /** Extracts the timecode information from a full-frame midi timecode message. | ||||
| @@ -773,8 +739,7 @@ public: | |||||
| int& frames, | int& frames, | ||||
| SmpteTimecodeType& timecodeType) const noexcept; | SmpteTimecodeType& timecodeType) const noexcept; | ||||
| /** Creates a full-frame MTC message. | |||||
| */ | |||||
| /** Creates a full-frame MTC message. */ | |||||
| static MidiMessage fullFrame (int hours, | static MidiMessage fullFrame (int hours, | ||||
| int minutes, | int minutes, | ||||
| int seconds, | int seconds, | ||||
| @@ -799,7 +764,6 @@ public: | |||||
| }; | }; | ||||
| /** Checks whether this is an MMC message. | /** Checks whether this is an MMC message. | ||||
| If it is, you can use the getMidiMachineControlCommand() to find out its type. | If it is, you can use the getMidiMachineControlCommand() to find out its type. | ||||
| */ | */ | ||||
| bool isMidiMachineControlMessage() const noexcept; | bool isMidiMachineControlMessage() const noexcept; | ||||
| @@ -65,7 +65,7 @@ struct JUCE_API AudioSourceChannelInfo | |||||
| Only the samples specified by the startSample and numSamples members of this structure | Only the samples specified by the startSample and numSamples members of this structure | ||||
| should be affected by the call. | should be affected by the call. | ||||
| The contents of the buffer when it is passed to the the AudioSource::getNextAudioBlock() | |||||
| The contents of the buffer when it is passed to the AudioSource::getNextAudioBlock() | |||||
| method can be treated as the input if the source is performing some kind of filter operation, | method can be treated as the input if the source is performing some kind of filter operation, | ||||
| but should be cleared if this is not the case - the clearActiveBufferRegion() is | but should be cleared if this is not the case - the clearActiveBufferRegion() is | ||||
| a handy way of doing this. | a handy way of doing this. | ||||
| @@ -114,7 +114,7 @@ void AudioDeviceManager::createDeviceTypesIfNeeded() | |||||
| { | { | ||||
| if (availableDeviceTypes.size() == 0) | if (availableDeviceTypes.size() == 0) | ||||
| { | { | ||||
| OwnedArray <AudioIODeviceType> types; | |||||
| OwnedArray<AudioIODeviceType> types; | |||||
| createAudioDeviceTypes (types); | createAudioDeviceTypes (types); | ||||
| for (int i = 0; i < types.size(); ++i) | for (int i = 0; i < types.size(); ++i) | ||||
| @@ -127,7 +127,7 @@ void AudioDeviceManager::createDeviceTypesIfNeeded() | |||||
| } | } | ||||
| } | } | ||||
| const OwnedArray <AudioIODeviceType>& AudioDeviceManager::getAvailableDeviceTypes() | |||||
| const OwnedArray<AudioIODeviceType>& AudioDeviceManager::getAvailableDeviceTypes() | |||||
| { | { | ||||
| scanDevicesIfNeeded(); | scanDevicesIfNeeded(); | ||||
| return availableDeviceTypes; | return availableDeviceTypes; | ||||
| @@ -147,13 +147,13 @@ void AudioDeviceManager::audioDeviceListChanged() | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| static void addIfNotNull (OwnedArray <AudioIODeviceType>& list, AudioIODeviceType* const device) | |||||
| static void addIfNotNull (OwnedArray<AudioIODeviceType>& list, AudioIODeviceType* const device) | |||||
| { | { | ||||
| if (device != nullptr) | if (device != nullptr) | ||||
| list.add (device); | list.add (device); | ||||
| } | } | ||||
| void AudioDeviceManager::createAudioDeviceTypes (OwnedArray <AudioIODeviceType>& list) | |||||
| void AudioDeviceManager::createAudioDeviceTypes (OwnedArray<AudioIODeviceType>& list) | |||||
| { | { | ||||
| addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI()); | addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI()); | ||||
| addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound()); | addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound()); | ||||
| @@ -181,7 +181,7 @@ void AudioDeviceManager::addAudioDeviceType (AudioIODeviceType* newDeviceType) | |||||
| //============================================================================== | //============================================================================== | ||||
| String AudioDeviceManager::initialise (const int numInputChannelsNeeded, | String AudioDeviceManager::initialise (const int numInputChannelsNeeded, | ||||
| const int numOutputChannelsNeeded, | const int numOutputChannelsNeeded, | ||||
| const XmlElement* const e, | |||||
| const XmlElement* const xml, | |||||
| const bool selectDefaultDeviceOnFailure, | const bool selectDefaultDeviceOnFailure, | ||||
| const String& preferredDefaultDeviceName, | const String& preferredDefaultDeviceName, | ||||
| const AudioDeviceSetup* preferredSetupOptions) | const AudioDeviceSetup* preferredSetupOptions) | ||||
| @@ -191,106 +191,127 @@ String AudioDeviceManager::initialise (const int numInputChannelsNeeded, | |||||
| numInputChansNeeded = numInputChannelsNeeded; | numInputChansNeeded = numInputChannelsNeeded; | ||||
| numOutputChansNeeded = numOutputChannelsNeeded; | numOutputChansNeeded = numOutputChannelsNeeded; | ||||
| if (e != nullptr && e->hasTagName ("DEVICESETUP")) | |||||
| { | |||||
| lastExplicitSettings = new XmlElement (*e); | |||||
| if (xml != nullptr && xml->hasTagName ("DEVICESETUP")) | |||||
| return initialiseFromXML (*xml, selectDefaultDeviceOnFailure, | |||||
| preferredDefaultDeviceName, preferredSetupOptions); | |||||
| String error; | |||||
| AudioDeviceSetup setup; | |||||
| return initialiseDefault (preferredDefaultDeviceName, preferredSetupOptions); | |||||
| } | |||||
| if (preferredSetupOptions != nullptr) | |||||
| setup = *preferredSetupOptions; | |||||
| String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDeviceName, | |||||
| const AudioDeviceSetup* preferredSetupOptions) | |||||
| { | |||||
| AudioDeviceSetup setup; | |||||
| if (e->getStringAttribute ("audioDeviceName").isNotEmpty()) | |||||
| { | |||||
| setup.inputDeviceName = setup.outputDeviceName | |||||
| = e->getStringAttribute ("audioDeviceName"); | |||||
| } | |||||
| else | |||||
| if (preferredSetupOptions != nullptr) | |||||
| { | |||||
| setup = *preferredSetupOptions; | |||||
| } | |||||
| else if (preferredDefaultDeviceName.isNotEmpty()) | |||||
| { | |||||
| for (int j = availableDeviceTypes.size(); --j >= 0;) | |||||
| { | { | ||||
| setup.inputDeviceName = e->getStringAttribute ("audioInputDeviceName"); | |||||
| setup.outputDeviceName = e->getStringAttribute ("audioOutputDeviceName"); | |||||
| } | |||||
| AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j); | |||||
| currentDeviceType = e->getStringAttribute ("deviceType"); | |||||
| const StringArray outs (type->getDeviceNames (false)); | |||||
| if (findType (currentDeviceType) == nullptr) | |||||
| { | |||||
| if (AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName)) | |||||
| currentDeviceType = type->getTypeName(); | |||||
| else if (availableDeviceTypes.size() > 0) | |||||
| currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); | |||||
| for (int i = 0; i < outs.size(); ++i) | |||||
| { | |||||
| if (outs[i].matchesWildcard (preferredDefaultDeviceName, true)) | |||||
| { | |||||
| setup.outputDeviceName = outs[i]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| const StringArray ins (type->getDeviceNames (true)); | |||||
| for (int i = 0; i < ins.size(); ++i) | |||||
| { | |||||
| if (ins[i].matchesWildcard (preferredDefaultDeviceName, true)) | |||||
| { | |||||
| setup.inputDeviceName = ins[i]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | |||||
| setup.bufferSize = e->getIntAttribute ("audioDeviceBufferSize"); | |||||
| setup.sampleRate = e->getDoubleAttribute ("audioDeviceRate"); | |||||
| insertDefaultDeviceNames (setup); | |||||
| return setAudioDeviceSetup (setup, false); | |||||
| } | |||||
| setup.inputChannels .parseString (e->getStringAttribute ("audioDeviceInChans", "11"), 2); | |||||
| setup.outputChannels.parseString (e->getStringAttribute ("audioDeviceOutChans", "11"), 2); | |||||
| String AudioDeviceManager::initialiseFromXML (const XmlElement& xml, | |||||
| const bool selectDefaultDeviceOnFailure, | |||||
| const String& preferredDefaultDeviceName, | |||||
| const AudioDeviceSetup* preferredSetupOptions) | |||||
| { | |||||
| lastExplicitSettings = new XmlElement (xml); | |||||
| setup.useDefaultInputChannels = ! e->hasAttribute ("audioDeviceInChans"); | |||||
| setup.useDefaultOutputChannels = ! e->hasAttribute ("audioDeviceOutChans"); | |||||
| String error; | |||||
| AudioDeviceSetup setup; | |||||
| error = setAudioDeviceSetup (setup, true); | |||||
| if (preferredSetupOptions != nullptr) | |||||
| setup = *preferredSetupOptions; | |||||
| midiInsFromXml.clear(); | |||||
| forEachXmlChildElementWithTagName (*e, c, "MIDIINPUT") | |||||
| midiInsFromXml.add (c->getStringAttribute ("name")); | |||||
| if (xml.getStringAttribute ("audioDeviceName").isNotEmpty()) | |||||
| { | |||||
| setup.inputDeviceName = setup.outputDeviceName | |||||
| = xml.getStringAttribute ("audioDeviceName"); | |||||
| } | |||||
| else | |||||
| { | |||||
| setup.inputDeviceName = xml.getStringAttribute ("audioInputDeviceName"); | |||||
| setup.outputDeviceName = xml.getStringAttribute ("audioOutputDeviceName"); | |||||
| } | |||||
| const StringArray allMidiIns (MidiInput::getDevices()); | |||||
| currentDeviceType = xml.getStringAttribute ("deviceType"); | |||||
| for (int i = allMidiIns.size(); --i >= 0;) | |||||
| setMidiInputEnabled (allMidiIns[i], midiInsFromXml.contains (allMidiIns[i])); | |||||
| if (findType (currentDeviceType) == nullptr) | |||||
| { | |||||
| if (AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName)) | |||||
| currentDeviceType = type->getTypeName(); | |||||
| else if (availableDeviceTypes.size() > 0) | |||||
| currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); | |||||
| } | |||||
| if (error.isNotEmpty() && selectDefaultDeviceOnFailure) | |||||
| error = initialise (numInputChannelsNeeded, numOutputChannelsNeeded, 0, | |||||
| false, preferredDefaultDeviceName); | |||||
| setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize"); | |||||
| setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate"); | |||||
| setDefaultMidiOutput (e->getStringAttribute ("defaultMidiOutput")); | |||||
| setup.inputChannels .parseString (xml.getStringAttribute ("audioDeviceInChans", "11"), 2); | |||||
| setup.outputChannels.parseString (xml.getStringAttribute ("audioDeviceOutChans", "11"), 2); | |||||
| return error; | |||||
| } | |||||
| else | |||||
| { | |||||
| AudioDeviceSetup setup; | |||||
| setup.useDefaultInputChannels = ! xml.hasAttribute ("audioDeviceInChans"); | |||||
| setup.useDefaultOutputChannels = ! xml.hasAttribute ("audioDeviceOutChans"); | |||||
| if (preferredSetupOptions != nullptr) | |||||
| { | |||||
| setup = *preferredSetupOptions; | |||||
| } | |||||
| else if (preferredDefaultDeviceName.isNotEmpty()) | |||||
| { | |||||
| for (int j = availableDeviceTypes.size(); --j >= 0;) | |||||
| { | |||||
| AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j); | |||||
| error = setAudioDeviceSetup (setup, true); | |||||
| const StringArray outs (type->getDeviceNames (false)); | |||||
| midiInsFromXml.clear(); | |||||
| for (int i = 0; i < outs.size(); ++i) | |||||
| { | |||||
| if (outs[i].matchesWildcard (preferredDefaultDeviceName, true)) | |||||
| { | |||||
| setup.outputDeviceName = outs[i]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| forEachXmlChildElementWithTagName (xml, c, "MIDIINPUT") | |||||
| midiInsFromXml.add (c->getStringAttribute ("name")); | |||||
| const StringArray ins (type->getDeviceNames (true)); | |||||
| const StringArray allMidiIns (MidiInput::getDevices()); | |||||
| for (int i = 0; i < ins.size(); ++i) | |||||
| { | |||||
| if (ins[i].matchesWildcard (preferredDefaultDeviceName, true)) | |||||
| { | |||||
| setup.inputDeviceName = ins[i]; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| for (int i = allMidiIns.size(); --i >= 0;) | |||||
| setMidiInputEnabled (allMidiIns[i], midiInsFromXml.contains (allMidiIns[i])); | |||||
| insertDefaultDeviceNames (setup); | |||||
| return setAudioDeviceSetup (setup, false); | |||||
| } | |||||
| if (error.isNotEmpty() && selectDefaultDeviceOnFailure) | |||||
| error = initialise (numInputChansNeeded, numOutputChansNeeded, | |||||
| nullptr, false, preferredDefaultDeviceName); | |||||
| setDefaultMidiOutput (xml.getStringAttribute ("defaultMidiOutput")); | |||||
| return error; | |||||
| } | |||||
| String AudioDeviceManager::initialiseWithDefaultDevices (int numInputChannelsNeeded, | |||||
| int numOutputChannelsNeeded) | |||||
| { | |||||
| lastExplicitSettings = nullptr; | |||||
| return initialise (numInputChannelsNeeded, numOutputChannelsNeeded, | |||||
| nullptr, false, String(), nullptr); | |||||
| } | } | ||||
| void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const | void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const | ||||
| @@ -880,7 +901,7 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) | |||||
| { | { | ||||
| if (defaultMidiOutputName != deviceName) | if (defaultMidiOutputName != deviceName) | ||||
| { | { | ||||
| Array <AudioIODeviceCallback*> oldCallbacks; | |||||
| Array<AudioIODeviceCallback*> oldCallbacks; | |||||
| { | { | ||||
| const ScopedLock sl (audioCallbackLock); | const ScopedLock sl (audioCallbackLock); | ||||
| @@ -915,7 +936,7 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) | |||||
| void AudioDeviceManager::playTestSound() | void AudioDeviceManager::playTestSound() | ||||
| { | { | ||||
| { // cunningly nested to swap, unlock and delete in that order. | { // cunningly nested to swap, unlock and delete in that order. | ||||
| ScopedPointer <AudioSampleBuffer> oldSound; | |||||
| ScopedPointer<AudioSampleBuffer> oldSound; | |||||
| { | { | ||||
| const ScopedLock sl (audioCallbackLock); | const ScopedLock sl (audioCallbackLock); | ||||
| @@ -191,7 +191,11 @@ public: | |||||
| const XmlElement* savedState, | const XmlElement* savedState, | ||||
| bool selectDefaultDeviceOnFailure, | bool selectDefaultDeviceOnFailure, | ||||
| const String& preferredDefaultDeviceName = String(), | const String& preferredDefaultDeviceName = String(), | ||||
| const AudioDeviceSetup* preferredSetupOptions = 0); | |||||
| const AudioDeviceSetup* preferredSetupOptions = nullptr); | |||||
| /** Resets everything to a default device setup, clearing any stored settings. */ | |||||
| String initialiseWithDefaultDevices (int numInputChannelsNeeded, | |||||
| int numOutputChannelsNeeded); | |||||
| /** Returns some XML representing the current state of the manager. | /** Returns some XML representing the current state of the manager. | ||||
| @@ -383,7 +387,7 @@ public: | |||||
| /** Returns a list of the types of device supported. | /** Returns a list of the types of device supported. | ||||
| */ | */ | ||||
| const OwnedArray <AudioIODeviceType>& getAvailableDeviceTypes(); | |||||
| const OwnedArray<AudioIODeviceType>& getAvailableDeviceTypes(); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Creates a list of available types. | /** Creates a list of available types. | ||||
| @@ -394,7 +398,7 @@ public: | |||||
| You can override this if your app needs to do something specific, like avoid | You can override this if your app needs to do something specific, like avoid | ||||
| using DirectSound devices, etc. | using DirectSound devices, etc. | ||||
| */ | */ | ||||
| virtual void createAudioDeviceTypes (OwnedArray <AudioIODeviceType>& types); | |||||
| virtual void createAudioDeviceTypes (OwnedArray<AudioIODeviceType>& types); | |||||
| /** Adds a new device type to the list of types. | /** Adds a new device type to the list of types. | ||||
| The manager will take ownership of the object that is passed-in. | The manager will take ownership of the object that is passed-in. | ||||
| @@ -446,30 +450,30 @@ public: | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| OwnedArray <AudioIODeviceType> availableDeviceTypes; | |||||
| OwnedArray <AudioDeviceSetup> lastDeviceTypeConfigs; | |||||
| OwnedArray<AudioIODeviceType> availableDeviceTypes; | |||||
| OwnedArray<AudioDeviceSetup> lastDeviceTypeConfigs; | |||||
| AudioDeviceSetup currentSetup; | AudioDeviceSetup currentSetup; | ||||
| ScopedPointer <AudioIODevice> currentAudioDevice; | |||||
| Array <AudioIODeviceCallback*> callbacks; | |||||
| ScopedPointer<AudioIODevice> currentAudioDevice; | |||||
| Array<AudioIODeviceCallback*> callbacks; | |||||
| int numInputChansNeeded, numOutputChansNeeded; | int numInputChansNeeded, numOutputChansNeeded; | ||||
| String currentDeviceType; | String currentDeviceType; | ||||
| BigInteger inputChannels, outputChannels; | BigInteger inputChannels, outputChannels; | ||||
| ScopedPointer <XmlElement> lastExplicitSettings; | |||||
| ScopedPointer<XmlElement> lastExplicitSettings; | |||||
| mutable bool listNeedsScanning; | mutable bool listNeedsScanning; | ||||
| bool useInputNames; | bool useInputNames; | ||||
| Atomic<int> inputLevelMeasurementEnabledCount; | Atomic<int> inputLevelMeasurementEnabledCount; | ||||
| double inputLevel; | double inputLevel; | ||||
| ScopedPointer <AudioSampleBuffer> testSound; | |||||
| ScopedPointer<AudioSampleBuffer> testSound; | |||||
| int testSoundPosition; | int testSoundPosition; | ||||
| AudioSampleBuffer tempBuffer; | AudioSampleBuffer tempBuffer; | ||||
| StringArray midiInsFromXml; | StringArray midiInsFromXml; | ||||
| OwnedArray <MidiInput> enabledMidiInputs; | |||||
| Array <MidiInputCallback*> midiCallbacks; | |||||
| OwnedArray<MidiInput> enabledMidiInputs; | |||||
| Array<MidiInputCallback*> midiCallbacks; | |||||
| StringArray midiCallbackDevices; | StringArray midiCallbackDevices; | ||||
| String defaultMidiOutputName; | String defaultMidiOutputName; | ||||
| ScopedPointer <MidiOutput> defaultMidiOutput; | |||||
| ScopedPointer<MidiOutput> defaultMidiOutput; | |||||
| CriticalSection audioCallbackLock, midiCallbackLock; | CriticalSection audioCallbackLock, midiCallbackLock; | ||||
| double cpuUsageMs, timeToCpuScale; | double cpuUsageMs, timeToCpuScale; | ||||
| @@ -500,6 +504,9 @@ private: | |||||
| double chooseBestSampleRate (double preferred) const; | double chooseBestSampleRate (double preferred) const; | ||||
| int chooseBestBufferSize (int preferred) const; | int chooseBestBufferSize (int preferred) const; | ||||
| void insertDefaultDeviceNames (AudioDeviceSetup&) const; | void insertDefaultDeviceNames (AudioDeviceSetup&) const; | ||||
| String initialiseDefault (const String& preferredDefaultDeviceName, const AudioDeviceSetup*); | |||||
| String initialiseFromXML (const XmlElement&, bool selectDefaultDeviceOnFailure, | |||||
| const String& preferredDefaultDeviceName, const AudioDeviceSetup*); | |||||
| AudioIODeviceType* findType (const String& inputName, const String& outputName); | AudioIODeviceType* findType (const String& inputName, const String& outputName); | ||||
| AudioIODeviceType* findType (const String& typeName); | AudioIODeviceType* findType (const String& typeName); | ||||
| @@ -118,7 +118,7 @@ public: | |||||
| /** | /** | ||||
| A class for receiving events when audio devices are inserted or removed. | A class for receiving events when audio devices are inserted or removed. | ||||
| You can register a AudioIODeviceType::Listener with an~AudioIODeviceType object | |||||
| You can register an AudioIODeviceType::Listener with an~AudioIODeviceType object | |||||
| using the AudioIODeviceType::addListener() method, and it will be called when | using the AudioIODeviceType::addListener() method, and it will be called when | ||||
| devices of that type are added or removed. | devices of that type are added or removed. | ||||
| @@ -123,6 +123,14 @@ private: | |||||
| { | { | ||||
| if (pendingBytes > 0 && *d >= 0x80) | if (pendingBytes > 0 && *d >= 0x80) | ||||
| { | { | ||||
| if (*d == 0xf7) | |||||
| { | |||||
| *dest++ = *d++; | |||||
| ++pendingBytes; | |||||
| --numBytes; | |||||
| break; | |||||
| } | |||||
| if (*d >= 0xfa || *d == 0xf8) | if (*d >= 0xfa || *d == 0xf8) | ||||
| { | { | ||||
| callback.handleIncomingMidiMessage (input, MidiMessage (*d, time)); | callback.handleIncomingMidiMessage (input, MidiMessage (*d, time)); | ||||
| @@ -131,11 +139,15 @@ private: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| if (*d == 0xf7) | |||||
| pendingBytes = 0; | |||||
| int used = 0; | |||||
| const MidiMessage m (d, numBytes, used, 0, time); | |||||
| if (used > 0) | |||||
| { | { | ||||
| *dest++ = *d++; | |||||
| pendingBytes++; | |||||
| --numBytes; | |||||
| callback.handleIncomingMidiMessage (input, m); | |||||
| numBytes -= used; | |||||
| d += used; | |||||
| } | } | ||||
| break; | break; | ||||
| @@ -144,7 +156,7 @@ private: | |||||
| else | else | ||||
| { | { | ||||
| *dest++ = *d++; | *dest++ = *d++; | ||||
| pendingBytes++; | |||||
| ++pendingBytes; | |||||
| --numBytes; | --numBytes; | ||||
| } | } | ||||
| } | } | ||||
| @@ -502,13 +502,9 @@ public: | |||||
| outputNames.clear(); | outputNames.clear(); | ||||
| outputIds.clear(); | outputIds.clear(); | ||||
| if (juce_libjackHandle == nullptr) | |||||
| { | |||||
| juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY); | |||||
| if (juce_libjackHandle == nullptr) | |||||
| return; | |||||
| } | |||||
| if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so.0", RTLD_LAZY); | |||||
| if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY); | |||||
| if (juce_libjackHandle == nullptr) return; | |||||
| jack_status_t status; | jack_status_t status; | ||||
| @@ -478,6 +478,12 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| midiOutShortMsg (handle->handle, *(unsigned int*) message.getRawData()); | |||||
| for (int i = 0; i < 50; ++i) | |||||
| { | |||||
| if (midiOutShortMsg (handle->handle, *(unsigned int*) message.getRawData()) != MIDIERR_NOTREADY) | |||||
| break; | |||||
| Sleep (1); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -43,7 +43,7 @@ class JUCE_API AudioSubsectionReader : public AudioFormatReader | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Creates a AudioSubsectionReader for a given data source. | |||||
| /** Creates an AudioSubsectionReader for a given data source. | |||||
| @param sourceReader the source reader from which we'll be taking data | @param sourceReader the source reader from which we'll be taking data | ||||
| @param subsectionStartSample the sample within the source reader which will be | @param subsectionStartSample the sample within the source reader which will be | ||||
| @@ -27,11 +27,11 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| #define JUCE_DECLARE_VST3_COM_REF_METHODS \ | #define JUCE_DECLARE_VST3_COM_REF_METHODS \ | ||||
| Steinberg::uint32 PLUGIN_API JUCE_CALLTYPE addRef() override { return (Steinberg::uint32) ++refCount; } \ | |||||
| Steinberg::uint32 PLUGIN_API JUCE_CALLTYPE release() override { const int r = --refCount; if (r == 0) delete this; return (Steinberg::uint32) r; } | |||||
| Steinberg::uint32 PLUGIN_API addRef() override { return (Steinberg::uint32) ++refCount; } \ | |||||
| Steinberg::uint32 PLUGIN_API release() override { const int r = --refCount; if (r == 0) delete this; return (Steinberg::uint32) r; } | |||||
| #define JUCE_DECLARE_VST3_COM_QUERY_METHODS \ | #define JUCE_DECLARE_VST3_COM_QUERY_METHODS \ | ||||
| Steinberg::tresult PLUGIN_API JUCE_CALLTYPE queryInterface (const Steinberg::TUID, void** obj) override \ | |||||
| Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID, void** obj) override \ | |||||
| { \ | { \ | ||||
| jassertfalse; \ | jassertfalse; \ | ||||
| *obj = nullptr; \ | *obj = nullptr; \ | ||||
| @@ -51,6 +51,14 @@ static bool doUIDsMatch (const Steinberg::TUID a, const Steinberg::TUID b) noexc | |||||
| return Steinberg::kResultOk; \ | return Steinberg::kResultOk; \ | ||||
| } | } | ||||
| #define TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID(CommonClassType, SourceClassType) \ | |||||
| if (doUIDsMatch (iid, CommonClassType::iid)) \ | |||||
| { \ | |||||
| addRef(); \ | |||||
| *obj = (CommonClassType*) static_cast<SourceClassType*> (this); \ | |||||
| return Steinberg::kResultOk; \ | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| static juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (string); } | static juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (string); } | ||||
| static juce::String toString (const Steinberg::char16* string) noexcept { return juce::String (juce::CharPointer_UTF16 ((juce::CharPointer_UTF16::CharType*) string)); } | static juce::String toString (const Steinberg::char16* string) noexcept { return juce::String (juce::CharPointer_UTF16 ((juce::CharPointer_UTF16::CharType*) string)); } | ||||
| @@ -70,19 +78,15 @@ static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept | |||||
| } | } | ||||
| #if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
| static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND; | |||||
| static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND; | |||||
| #else | #else | ||||
| static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeNSView; | |||||
| static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeNSView; | |||||
| #endif | #endif | ||||
| //============================================================================== | //============================================================================== | ||||
| /** The equivalent numChannels and speaker arrangements should always | |||||
| match between this function and fillWithCorrespondingSpeakerArrangements(). | |||||
| There can only be 1 arrangement per channel count. (i.e.: 4 channels == k31Cine OR k40Cine) | |||||
| @see fillWithCorrespondingSpeakerArrangements | |||||
| /** For the sake of simplicity, there can only be 1 arrangement type per channel count. | |||||
| i.e.: 4 channels == k31Cine OR k40Cine | |||||
| */ | */ | ||||
| static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numChannels) noexcept | static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numChannels) noexcept | ||||
| { | { | ||||
| @@ -116,53 +120,6 @@ static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numC | |||||
| return (Steinberg::Vst::SpeakerArrangement) bi.toInt64(); | return (Steinberg::Vst::SpeakerArrangement) bi.toInt64(); | ||||
| } | } | ||||
| /** The equivalent numChannels and speaker arrangements should always | |||||
| match between this function and getArrangementForNumChannels(). | |||||
| There can only be 1 arrangement per channel count. (i.e.: 4 channels == k31Cine OR k40Cine) | |||||
| @see getArrangementForNumChannels | |||||
| */ | |||||
| static void fillWithCorrespondingSpeakerArrangements (Array<Steinberg::Vst::SpeakerArrangement>& destination, | |||||
| int numChannels) | |||||
| { | |||||
| using namespace Steinberg::Vst::SpeakerArr; | |||||
| destination.clearQuick(); | |||||
| if (numChannels <= 0) | |||||
| { | |||||
| destination.add (kEmpty); | |||||
| return; | |||||
| } | |||||
| // The order of the arrangement checks must be descending, since most plugins test for | |||||
| /// the first arrangement to match their number of specified channels. | |||||
| if (numChannels > 24) | |||||
| { | |||||
| juce::BigInteger bi; | |||||
| bi.setRange (0, jmin (numChannels, (int) (sizeof (Steinberg::Vst::SpeakerArrangement) * 8)), true); | |||||
| destination.add ((Steinberg::Vst::SpeakerArrangement) bi.toInt64()); | |||||
| } | |||||
| if (numChannels >= 24) destination.add ((Steinberg::Vst::SpeakerArrangement) 1929904127); // k222 | |||||
| if (numChannels >= 14) destination.add (k131); | |||||
| if (numChannels >= 13) destination.add (k130); | |||||
| if (numChannels >= 12) destination.add (k111); | |||||
| if (numChannels >= 11) destination.add (k101); | |||||
| if (numChannels >= 10) destination.add (k91); | |||||
| if (numChannels >= 9) destination.add (k90); | |||||
| if (numChannels >= 8) destination.add (k71CineFullFront); | |||||
| if (numChannels >= 7) destination.add (k61Cine); | |||||
| if (numChannels >= 6) destination.add (k51); | |||||
| if (numChannels >= 5) destination.add (k50); | |||||
| if (numChannels >= 4) destination.add (k31Cine); | |||||
| if (numChannels >= 3) destination.add (k30Cine); | |||||
| if (numChannels >= 2) destination.add (kStereo); | |||||
| if (numChannels >= 1) destination.add (kMono); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| template <class ObjectType> | template <class ObjectType> | ||||
| class ComSmartPtr | class ComSmartPtr | ||||
| @@ -98,7 +98,7 @@ static void fillDescriptionWith (PluginDescription& description, ObjectType& obj | |||||
| description.version = toString (object.version).trim(); | description.version = toString (object.version).trim(); | ||||
| description.category = toString (object.subCategories).trim(); | description.category = toString (object.subCategories).trim(); | ||||
| if (description.manufacturerName.isEmpty()) | |||||
| if (description.manufacturerName.trim().isEmpty()) | |||||
| description.manufacturerName = toString (object.vendor).trim(); | description.manufacturerName = toString (object.vendor).trim(); | ||||
| } | } | ||||
| @@ -459,13 +459,13 @@ public: | |||||
| { | { | ||||
| *obj = nullptr; | *obj = nullptr; | ||||
| if (! doIdsMatch (cid, iid)) | |||||
| if (! doUIDsMatch (cid, iid)) | |||||
| { | { | ||||
| jassertfalse; | jassertfalse; | ||||
| return kInvalidArgument; | return kInvalidArgument; | ||||
| } | } | ||||
| if (doIdsMatch (cid, Vst::IMessage::iid) && doIdsMatch (iid, Vst::IMessage::iid)) | |||||
| if (doUIDsMatch (cid, Vst::IMessage::iid) && doUIDsMatch (iid, Vst::IMessage::iid)) | |||||
| { | { | ||||
| ComSmartPtr<Message> m (new Message (*this, attributeList)); | ComSmartPtr<Message> m (new Message (*this, attributeList)); | ||||
| messageQueue.add (m); | messageQueue.add (m); | ||||
| @@ -473,7 +473,7 @@ public: | |||||
| *obj = m; | *obj = m; | ||||
| return kResultOk; | return kResultOk; | ||||
| } | } | ||||
| else if (doIdsMatch (cid, Vst::IAttributeList::iid) && doIdsMatch (iid, Vst::IAttributeList::iid)) | |||||
| else if (doUIDsMatch (cid, Vst::IAttributeList::iid) && doUIDsMatch (iid, Vst::IAttributeList::iid)) | |||||
| { | { | ||||
| ComSmartPtr<AttributeList> l (new AttributeList (this)); | ComSmartPtr<AttributeList> l (new AttributeList (this)); | ||||
| l->addRef(); | l->addRef(); | ||||
| @@ -526,7 +526,7 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| tresult PLUGIN_API queryInterface (const TUID iid, void** obj) override | tresult PLUGIN_API queryInterface (const TUID iid, void** obj) override | ||||
| { | { | ||||
| if (doIdsMatch (iid, Vst::IAttributeList::iid)) | |||||
| if (doUIDsMatch (iid, Vst::IAttributeList::iid)) | |||||
| { | { | ||||
| *obj = attributeList.get(); | *obj = attributeList.get(); | ||||
| return kResultOk; | return kResultOk; | ||||
| @@ -539,6 +539,7 @@ public: | |||||
| TEST_FOR_AND_RETURN_IF_VALID (Vst::IHostApplication) | TEST_FOR_AND_RETURN_IF_VALID (Vst::IHostApplication) | ||||
| TEST_FOR_AND_RETURN_IF_VALID (Vst::IParamValueQueue) | TEST_FOR_AND_RETURN_IF_VALID (Vst::IParamValueQueue) | ||||
| TEST_FOR_AND_RETURN_IF_VALID (Vst::IUnitHandler) | TEST_FOR_AND_RETURN_IF_VALID (Vst::IUnitHandler) | ||||
| TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (FUnknown, Vst::IComponentHandler) | |||||
| *obj = nullptr; | *obj = nullptr; | ||||
| return kNotImplemented; | return kNotImplemented; | ||||
| @@ -550,12 +551,6 @@ private: | |||||
| String appName; | String appName; | ||||
| VST3PluginInstance* owner; | VST3PluginInstance* owner; | ||||
| //============================================================================== | |||||
| static bool doIdsMatch (const TUID a, const TUID b) noexcept | |||||
| { | |||||
| return std::memcmp (a, b, sizeof (TUID)) == 0; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| class Message : public Vst::IMessage | class Message : public Vst::IMessage | ||||
| { | { | ||||
| @@ -1206,7 +1201,7 @@ public: | |||||
| setOpaque (true); | setOpaque (true); | ||||
| setVisible (true); | setVisible (true); | ||||
| view->setFrame (this); | |||||
| warnOnFailure (view->setFrame (this)); | |||||
| ViewRect rect; | ViewRect rect; | ||||
| warnOnFailure (view->getSize (&rect)); | warnOnFailure (view->getSize (&rect)); | ||||
| @@ -1215,7 +1210,7 @@ public: | |||||
| ~VST3PluginWindow() | ~VST3PluginWindow() | ||||
| { | { | ||||
| view->removed(); | |||||
| warnOnFailure (view->removed()); | |||||
| getAudioProcessor()->editorBeingDeleted (this); | getAudioProcessor()->editorBeingDeleted (this); | ||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| @@ -1273,7 +1268,7 @@ public: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| view->getSize (&rect); | |||||
| warnOnFailure (view->getSize (&rect)); | |||||
| } | } | ||||
| #if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
| @@ -1485,12 +1480,6 @@ public: | |||||
| { | { | ||||
| using namespace Vst; | using namespace Vst; | ||||
| const int numInputs = getNumInputChannels(); | |||||
| const int numOutputs = getNumOutputChannels(); | |||||
| // Needed for having the same sample rate in processBlock(); some plugins need this! | |||||
| setPlayConfigDetails (numInputs, numOutputs, sampleRate, estimatedSamplesPerBlock); | |||||
| ProcessSetup setup; | ProcessSetup setup; | ||||
| setup.symbolicSampleSize = kSample32; | setup.symbolicSampleSize = kSample32; | ||||
| setup.maxSamplesPerBlock = estimatedSamplesPerBlock; | setup.maxSamplesPerBlock = estimatedSamplesPerBlock; | ||||
| @@ -1504,16 +1493,28 @@ public: | |||||
| editController->setComponentHandler (host); | editController->setComponentHandler (host); | ||||
| setStateForAllBusses (true); | |||||
| Array<SpeakerArrangement> inArrangements, outArrangements; | Array<SpeakerArrangement> inArrangements, outArrangements; | ||||
| fillWithCorrespondingSpeakerArrangements (inArrangements, numInputs); | |||||
| fillWithCorrespondingSpeakerArrangements (outArrangements, numOutputs); | |||||
| for (int i = 0; i < numInputAudioBusses; ++i) | |||||
| inArrangements.add (getArrangementForNumChannels (jmax (0, (int) getBusInfo (true, true, i).channelCount))); | |||||
| for (int i = 0; i < numOutputAudioBusses; ++i) | |||||
| outArrangements.add (getArrangementForNumChannels (jmax (0, (int) getBusInfo (false, true, i).channelCount))); | |||||
| warnOnFailure (processor->setBusArrangements (inArrangements.getRawDataPointer(), numInputAudioBusses, | warnOnFailure (processor->setBusArrangements (inArrangements.getRawDataPointer(), numInputAudioBusses, | ||||
| outArrangements.getRawDataPointer(), numOutputAudioBusses)); | outArrangements.getRawDataPointer(), numOutputAudioBusses)); | ||||
| // Update the num. busses in case the configuration has been modified by the plugin. (May affect number of channels!): | |||||
| numInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true); | |||||
| numOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true); | |||||
| // Needed for having the same sample rate in processBlock(); some plugins need this! | |||||
| setPlayConfigDetails (getNumSingleDirectionChannelsFor (component, true, true), | |||||
| getNumSingleDirectionChannelsFor (component, false, true), | |||||
| sampleRate, estimatedSamplesPerBlock); | |||||
| setStateForAllBusses (true); | |||||
| warnOnFailure (component->setActive (true)); | warnOnFailure (component->setActive (true)); | ||||
| warnOnFailure (processor->setProcessing (true)); | warnOnFailure (processor->setProcessing (true)); | ||||
| } | } | ||||
| @@ -1716,7 +1717,7 @@ public: | |||||
| if (head != nullptr) | if (head != nullptr) | ||||
| { | { | ||||
| ScopedPointer<Steinberg::MemoryStream> s (createMemoryStreamForState (*head, "IComponent")); | |||||
| ComSmartPtr<Steinberg::MemoryStream> s (createMemoryStreamForState (*head, "IComponent")); | |||||
| if (s != nullptr && component != nullptr) | if (s != nullptr && component != nullptr) | ||||
| component->setState (s); | component->setState (s); | ||||
| @@ -119,15 +119,18 @@ struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewCompone | |||||
| { | { | ||||
| if (NSView* parent = (NSView*) getView()) | if (NSView* parent = (NSView*) getView()) | ||||
| { | { | ||||
| if (NSView* child = [[parent subviews] firstObject]) | |||||
| if ([[parent subviews] count] > 0) | |||||
| { | { | ||||
| NSRect f = [parent frame]; | |||||
| NSSize newSize = [child frame].size; | |||||
| if (f.size.width != newSize.width || f.size.height != newSize.height) | |||||
| if (NSView* child = [[parent subviews] objectAtIndex: 0]) | |||||
| { | { | ||||
| f.size = newSize; | |||||
| [parent setFrame: f]; | |||||
| NSRect f = [parent frame]; | |||||
| NSSize newSize = [child frame].size; | |||||
| if (f.size.width != newSize.width || f.size.height != newSize.height) | |||||
| { | |||||
| f.size = newSize; | |||||
| [parent setFrame: f]; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -102,7 +102,7 @@ public: | |||||
| /** True if the plug-in is part of a multi-type container, e.g. a VST Shell. */ | /** True if the plug-in is part of a multi-type container, e.g. a VST Shell. */ | ||||
| bool hasSharedContainer; | bool hasSharedContainer; | ||||
| /** Returns true if the two descriptions refer the the same plug-in. | |||||
| /** Returns true if the two descriptions refer to the same plug-in. | |||||
| This isn't quite as simple as them just having the same file (because of | This isn't quite as simple as them just having the same file (because of | ||||
| shell plug-ins). | shell plug-ins). | ||||
| @@ -76,14 +76,14 @@ public: | |||||
| /** Changes or adds a named value. | /** Changes or adds a named value. | ||||
| @returns true if a value was changed or added; false if the | @returns true if a value was changed or added; false if the | ||||
| value was already set the the value passed-in. | |||||
| value was already set the value passed-in. | |||||
| */ | */ | ||||
| bool set (Identifier name, const var& newValue); | bool set (Identifier name, const var& newValue); | ||||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | ||||
| /** Changes or adds a named value. | /** Changes or adds a named value. | ||||
| @returns true if a value was changed or added; false if the | @returns true if a value was changed or added; false if the | ||||
| value was already set the the value passed-in. | |||||
| value was already set the value passed-in. | |||||
| */ | */ | ||||
| bool set (Identifier name, var&& newValue); | bool set (Identifier name, var&& newValue); | ||||
| #endif | #endif | ||||
| @@ -133,7 +133,6 @@ namespace juce | |||||
| #include "network/juce_MACAddress.cpp" | #include "network/juce_MACAddress.cpp" | ||||
| #include "network/juce_NamedPipe.cpp" | #include "network/juce_NamedPipe.cpp" | ||||
| #include "network/juce_Socket.cpp" | #include "network/juce_Socket.cpp" | ||||
| #include "network/juce_URL.cpp" | |||||
| #include "network/juce_IPAddress.cpp" | #include "network/juce_IPAddress.cpp" | ||||
| #include "streams/juce_BufferedInputStream.cpp" | #include "streams/juce_BufferedInputStream.cpp" | ||||
| #include "streams/juce_FileInputSource.cpp" | #include "streams/juce_FileInputSource.cpp" | ||||
| @@ -219,5 +218,6 @@ namespace juce | |||||
| #include "threads/juce_ChildProcess.cpp" | #include "threads/juce_ChildProcess.cpp" | ||||
| #include "threads/juce_HighResolutionTimer.cpp" | #include "threads/juce_HighResolutionTimer.cpp" | ||||
| #include "network/juce_URL.cpp" | |||||
| } | } | ||||
| @@ -64,7 +64,7 @@ | |||||
| //============================================================================= | //============================================================================= | ||||
| /** Config: JUCE_LOG_ASSERTIONS | /** Config: JUCE_LOG_ASSERTIONS | ||||
| If this flag is enabled, the the jassert and jassertfalse macros will always use Logger::writeToLog() | |||||
| If this flag is enabled, the jassert and jassertfalse macros will always use Logger::writeToLog() | |||||
| to write a message when an assertion happens. | to write a message when an assertion happens. | ||||
| Enabling it will also leave this turned on in release builds. When it's disabled, | Enabling it will also leave this turned on in release builds. When it's disabled, | ||||
| @@ -589,10 +589,34 @@ public final class JuceAppActivity extends Activity | |||||
| //============================================================================== | //============================================================================== | ||||
| public static class HTTPStream | public static class HTTPStream | ||||
| { | { | ||||
| public HTTPStream (HttpURLConnection connection_) throws IOException | |||||
| public HTTPStream (HttpURLConnection connection_, | |||||
| int[] statusCode, StringBuffer responseHeaders) throws IOException | |||||
| { | { | ||||
| connection = connection_; | connection = connection_; | ||||
| inputStream = new BufferedInputStream (connection.getInputStream()); | |||||
| try | |||||
| { | |||||
| inputStream = new BufferedInputStream (connection.getInputStream()); | |||||
| } | |||||
| catch (IOException e) | |||||
| { | |||||
| if (connection.getResponseCode() < org.apache.http.HttpStatus.SC_BAD_REQUEST) | |||||
| throw e; | |||||
| } | |||||
| finally | |||||
| { | |||||
| statusCode[0] = connection.getResponseCode(); | |||||
| } | |||||
| if (statusCode[0] >= org.apache.http.HttpStatus.SC_BAD_REQUEST) | |||||
| inputStream = connection.getErrorStream(); | |||||
| else | |||||
| inputStream = connection.getInputStream(); | |||||
| for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet()) | |||||
| if (entry.getKey() != null && entry.getValue() != null) | |||||
| responseHeaders.append (entry.getKey() + ": " | |||||
| + android.text.TextUtils.join (",", entry.getValue()) + "\n"); | |||||
| } | } | ||||
| public final void release() | public final void release() | ||||
| @@ -634,30 +658,31 @@ public final class JuceAppActivity extends Activity | |||||
| private long position; | private long position; | ||||
| } | } | ||||
| public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, | |||||
| String headers, int timeOutMs, | |||||
| java.lang.StringBuffer responseHeaders) | |||||
| public static final HTTPStream createHTTPStream (String address, | |||||
| boolean isPost, byte[] postData, String headers, | |||||
| int timeOutMs, int[] statusCode, | |||||
| StringBuffer responseHeaders) | |||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| HttpURLConnection connection = (HttpURLConnection) (new URL (address).openConnection()); | |||||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address) | |||||
| .openConnection()); | |||||
| if (connection != null) | if (connection != null) | ||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| if (isPost) | if (isPost) | ||||
| { | { | ||||
| connection.setConnectTimeout (timeOutMs); | |||||
| connection.setDoOutput (true); | |||||
| connection.setChunkedStreamingMode (0); | |||||
| connection.setRequestMethod("POST"); | |||||
| connection.setConnectTimeout(timeOutMs); | |||||
| connection.setDoOutput(true); | |||||
| connection.setChunkedStreamingMode(0); | |||||
| OutputStream out = connection.getOutputStream(); | OutputStream out = connection.getOutputStream(); | ||||
| out.write (postData); | |||||
| out.write(postData); | |||||
| out.flush(); | out.flush(); | ||||
| } | } | ||||
| return new HTTPStream (connection); | |||||
| return new HTTPStream (connection, statusCode, responseHeaders); | |||||
| } | } | ||||
| catch (Throwable e) | catch (Throwable e) | ||||
| { | { | ||||
| @@ -665,8 +690,7 @@ public final class JuceAppActivity extends Activity | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| catch (Throwable e) | |||||
| {} | |||||
| catch (Throwable e) {} | |||||
| return null; | return null; | ||||
| } | } | ||||
| @@ -251,7 +251,7 @@ extern AndroidSystem android; | |||||
| class ThreadLocalJNIEnvHolder | class ThreadLocalJNIEnvHolder | ||||
| { | { | ||||
| public: | public: | ||||
| ThreadLocalJNIEnvHolder() | |||||
| ThreadLocalJNIEnvHolder() noexcept | |||||
| : jvm (nullptr) | : jvm (nullptr) | ||||
| { | { | ||||
| zeromem (threads, sizeof (threads)); | zeromem (threads, sizeof (threads)); | ||||
| @@ -269,18 +269,19 @@ public: | |||||
| addEnv (env); | addEnv (env); | ||||
| } | } | ||||
| JNIEnv* attach() | |||||
| JNIEnv* attach() noexcept | |||||
| { | { | ||||
| JNIEnv* env = nullptr; | |||||
| jvm->AttachCurrentThread (&env, nullptr); | |||||
| if (env != nullptr) | |||||
| addEnv (env); | |||||
| if (JNIEnv* env = attachToCurrentThread()) | |||||
| { | |||||
| SpinLock::ScopedLockType sl (addRemoveLock); | |||||
| return addEnv (env); | |||||
| } | |||||
| return env; | |||||
| jassertfalse; | |||||
| return nullptr; | |||||
| } | } | ||||
| void detach() | |||||
| void detach() noexcept | |||||
| { | { | ||||
| jvm->DetachCurrentThread(); | jvm->DetachCurrentThread(); | ||||
| @@ -294,54 +295,61 @@ public: | |||||
| JNIEnv* getOrAttach() noexcept | JNIEnv* getOrAttach() noexcept | ||||
| { | { | ||||
| JNIEnv* env = get(); | |||||
| if (JNIEnv* env = get()) | |||||
| return env; | |||||
| if (env == nullptr) | |||||
| env = attach(); | |||||
| SpinLock::ScopedLockType sl (addRemoveLock); | |||||
| jassert (env != nullptr); | |||||
| return env; | |||||
| } | |||||
| if (JNIEnv* env = get()) | |||||
| return env; | |||||
| JNIEnv* get() const noexcept | |||||
| { | |||||
| const pthread_t thisThread = pthread_self(); | |||||
| for (int i = 0; i < maxThreads; ++i) | |||||
| if (threads[i] == thisThread) | |||||
| return envs[i]; | |||||
| if (JNIEnv* env = attachToCurrentThread()) | |||||
| return addEnv (env); | |||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| enum { maxThreads = 32 }; | |||||
| private: | private: | ||||
| JavaVM* jvm; | JavaVM* jvm; | ||||
| enum { maxThreads = 32 }; | |||||
| pthread_t threads [maxThreads]; | pthread_t threads [maxThreads]; | ||||
| JNIEnv* envs [maxThreads]; | JNIEnv* envs [maxThreads]; | ||||
| SpinLock addRemoveLock; | SpinLock addRemoveLock; | ||||
| void addEnv (JNIEnv* env) | |||||
| JNIEnv* addEnv (JNIEnv* env) noexcept | |||||
| { | { | ||||
| SpinLock::ScopedLockType sl (addRemoveLock); | |||||
| const pthread_t thisThread = pthread_self(); | |||||
| if (get() == nullptr) | |||||
| for (int i = 0; i < maxThreads; ++i) | |||||
| { | { | ||||
| const pthread_t thisThread = pthread_self(); | |||||
| for (int i = 0; i < maxThreads; ++i) | |||||
| if (threads[i] == 0) | |||||
| { | { | ||||
| if (threads[i] == 0) | |||||
| { | |||||
| envs[i] = env; | |||||
| threads[i] = thisThread; | |||||
| return; | |||||
| } | |||||
| envs[i] = env; | |||||
| threads[i] = thisThread; | |||||
| return env; | |||||
| } | } | ||||
| } | } | ||||
| jassertfalse; // too many threads! | jassertfalse; // too many threads! | ||||
| return nullptr; | |||||
| } | |||||
| JNIEnv* get() const noexcept | |||||
| { | |||||
| const pthread_t thisThread = pthread_self(); | |||||
| for (int i = 0; i < maxThreads; ++i) | |||||
| if (threads[i] == thisThread) | |||||
| return envs[i]; | |||||
| return nullptr; | |||||
| } | |||||
| JNIEnv* attachToCurrentThread() | |||||
| { | |||||
| JNIEnv* env = nullptr; | |||||
| jvm->AttachCurrentThread (&env, nullptr); | |||||
| return env; | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -357,7 +365,7 @@ extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder; | |||||
| METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \ | METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \ | ||||
| METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \ | METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \ | ||||
| METHOD (renderGlyph, "renderGlyph", "(CLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \ | METHOD (renderGlyph, "renderGlyph", "(CLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \ | ||||
| STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;ILjava/lang/StringBuffer;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ | |||||
| STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ | |||||
| METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \ | METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \ | ||||
| METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ | METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ | ||||
| METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ | METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ | ||||
| @@ -71,6 +71,7 @@ public: | |||||
| WebInputStream (String address, bool isPost, const MemoryBlock& postData, | WebInputStream (String address, bool isPost, const MemoryBlock& postData, | ||||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | ||||
| const String& headers, int timeOutMs, StringPairArray* responseHeaders) | const String& headers, int timeOutMs, StringPairArray* responseHeaders) | ||||
| : statusCode (0) | |||||
| { | { | ||||
| if (! address.contains ("://")) | if (! address.contains ("://")) | ||||
| address = "http://" + address; | address = "http://" + address; | ||||
| @@ -91,6 +92,9 @@ public: | |||||
| // thread. You'll need to move your networking code to a background thread to keep it happy.. | // thread. You'll need to move your networking code to a background thread to keep it happy.. | ||||
| jassert (Thread::getCurrentThread() != nullptr); | jassert (Thread::getCurrentThread() != nullptr); | ||||
| jintArray statusCodeArray = env->NewIntArray (1); | |||||
| jassert (statusCodeArray != 0); | |||||
| stream = GlobalRef (env->CallStaticObjectMethod (JuceAppActivity, | stream = GlobalRef (env->CallStaticObjectMethod (JuceAppActivity, | ||||
| JuceAppActivity.createHTTPStream, | JuceAppActivity.createHTTPStream, | ||||
| javaString (address).get(), | javaString (address).get(), | ||||
| @@ -98,8 +102,14 @@ public: | |||||
| postDataArray, | postDataArray, | ||||
| javaString (headers).get(), | javaString (headers).get(), | ||||
| (jint) timeOutMs, | (jint) timeOutMs, | ||||
| statusCodeArray, | |||||
| responseHeaderBuffer.get())); | responseHeaderBuffer.get())); | ||||
| jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0); | |||||
| statusCode = statusCodeElements[0]; | |||||
| env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0); | |||||
| env->DeleteLocalRef (statusCodeArray); | |||||
| if (postDataArray != 0) | if (postDataArray != 0) | ||||
| env->DeleteLocalRef (postDataArray); | env->DeleteLocalRef (postDataArray); | ||||
| @@ -135,6 +145,8 @@ public: | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| bool isError() const { return stream == nullptr; } | |||||
| bool isExhausted() override { return stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted); } | bool isExhausted() override { return stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted); } | ||||
| int64 getTotalLength() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0; } | int64 getTotalLength() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0; } | ||||
| int64 getPosition() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0; } | int64 getPosition() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0; } | ||||
| @@ -162,18 +174,8 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| GlobalRef stream; | GlobalRef stream; | ||||
| int statusCode; | |||||
| private: | private: | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | ||||
| }; | }; | ||||
| InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, | |||||
| OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||||
| const String& headers, const int timeOutMs, StringPairArray* responseHeaders) | |||||
| { | |||||
| ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData, | |||||
| progressCallback, progressCallbackContext, | |||||
| headers, timeOutMs, responseHeaders)); | |||||
| return wi->stream != 0 ? wi.release() : nullptr; | |||||
| } | |||||
| @@ -72,11 +72,11 @@ public: | |||||
| WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | ||||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | ||||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | ||||
| : socketHandle (-1), levelsOfRedirection (0), | |||||
| : statusCode (0), socketHandle (-1), levelsOfRedirection (0), | |||||
| address (address_), headers (headers_), postData (postData_), position (0), | address (address_), headers (headers_), postData (postData_), position (0), | ||||
| finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | ||||
| { | { | ||||
| createConnection (progressCallback, progressCallbackContext); | |||||
| statusCode = createConnection (progressCallback, progressCallbackContext); | |||||
| if (responseHeaders != nullptr && ! isError()) | if (responseHeaders != nullptr && ! isError()) | ||||
| { | { | ||||
| @@ -144,7 +144,7 @@ public: | |||||
| { | { | ||||
| closeSocket(); | closeSocket(); | ||||
| position = 0; | position = 0; | ||||
| createConnection (0, 0); | |||||
| statusCode = createConnection (0, 0); | |||||
| } | } | ||||
| skipNextBytes (wantedPos - position); | skipNextBytes (wantedPos - position); | ||||
| @@ -154,6 +154,8 @@ public: | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| int statusCode; | |||||
| private: | private: | ||||
| int socketHandle, levelsOfRedirection; | int socketHandle, levelsOfRedirection; | ||||
| StringArray headerLines; | StringArray headerLines; | ||||
| @@ -173,7 +175,7 @@ private: | |||||
| levelsOfRedirection = 0; | levelsOfRedirection = 0; | ||||
| } | } | ||||
| void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) | |||||
| int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) | |||||
| { | { | ||||
| closeSocket(); | closeSocket(); | ||||
| @@ -189,7 +191,7 @@ private: | |||||
| String hostName, hostPath; | String hostName, hostPath; | ||||
| int hostPort; | int hostPort; | ||||
| if (! decomposeURL (address, hostName, hostPath, hostPort)) | if (! decomposeURL (address, hostName, hostPath, hostPort)) | ||||
| return; | |||||
| return 0; | |||||
| String serverName, proxyName, proxyPath; | String serverName, proxyName, proxyPath; | ||||
| int proxyPort = 0; | int proxyPort = 0; | ||||
| @@ -199,7 +201,7 @@ private: | |||||
| if (proxyURL.startsWithIgnoreCase ("http://")) | if (proxyURL.startsWithIgnoreCase ("http://")) | ||||
| { | { | ||||
| if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | ||||
| return; | |||||
| return 0; | |||||
| serverName = proxyName; | serverName = proxyName; | ||||
| port = proxyPort; | port = proxyPort; | ||||
| @@ -219,14 +221,14 @@ private: | |||||
| struct addrinfo* result = nullptr; | struct addrinfo* result = nullptr; | ||||
| if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0) | if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0) | ||||
| return; | |||||
| return 0; | |||||
| socketHandle = socket (result->ai_family, result->ai_socktype, 0); | socketHandle = socket (result->ai_family, result->ai_socktype, 0); | ||||
| if (socketHandle == -1) | if (socketHandle == -1) | ||||
| { | { | ||||
| freeaddrinfo (result); | freeaddrinfo (result); | ||||
| return; | |||||
| return 0; | |||||
| } | } | ||||
| int receiveBufferSize = 16384; | int receiveBufferSize = 16384; | ||||
| @@ -241,7 +243,7 @@ private: | |||||
| { | { | ||||
| closeSocket(); | closeSocket(); | ||||
| freeaddrinfo (result); | freeaddrinfo (result); | ||||
| return; | |||||
| return 0; | |||||
| } | } | ||||
| freeaddrinfo (result); | freeaddrinfo (result); | ||||
| @@ -254,7 +256,7 @@ private: | |||||
| progressCallback, progressCallbackContext)) | progressCallback, progressCallbackContext)) | ||||
| { | { | ||||
| closeSocket(); | closeSocket(); | ||||
| return; | |||||
| return 0; | |||||
| } | } | ||||
| } | } | ||||
| @@ -265,15 +267,15 @@ private: | |||||
| { | { | ||||
| headerLines = StringArray::fromLines (responseHeader); | headerLines = StringArray::fromLines (responseHeader); | ||||
| const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false) | |||||
| .substring (0, 3).getIntValue(); | |||||
| const int status = responseHeader.fromFirstOccurrenceOf (" ", false, false) | |||||
| .substring (0, 3).getIntValue(); | |||||
| //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); | //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); | ||||
| //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); | //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); | ||||
| String location (findHeaderItem (headerLines, "Location:")); | String location (findHeaderItem (headerLines, "Location:")); | ||||
| if (statusCode >= 300 && statusCode < 400 | |||||
| if (status >= 300 && status < 400 | |||||
| && location.isNotEmpty() && location != address) | && location.isNotEmpty() && location != address) | ||||
| { | { | ||||
| if (! location.startsWithIgnoreCase ("http://")) | if (! location.startsWithIgnoreCase ("http://")) | ||||
| @@ -282,18 +284,18 @@ private: | |||||
| if (++levelsOfRedirection <= 3) | if (++levelsOfRedirection <= 3) | ||||
| { | { | ||||
| address = location; | address = location; | ||||
| createConnection (progressCallback, progressCallbackContext); | |||||
| return; | |||||
| return createConnection (progressCallback, progressCallbackContext); | |||||
| } | } | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| levelsOfRedirection = 0; | levelsOfRedirection = 0; | ||||
| return; | |||||
| return status; | |||||
| } | } | ||||
| } | } | ||||
| closeSocket(); | closeSocket(); | ||||
| return 0; | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -437,14 +439,3 @@ private: | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | ||||
| }; | }; | ||||
| InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, | |||||
| OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||||
| const String& headers, const int timeOutMs, StringPairArray* responseHeaders) | |||||
| { | |||||
| ScopedPointer<WebInputStream> wi (new WebInputStream (address, isPost, postData, | |||||
| progressCallback, progressCallbackContext, | |||||
| headers, timeOutMs, responseHeaders)); | |||||
| return wi->isError() ? nullptr : wi.release(); | |||||
| } | |||||
| @@ -118,6 +118,7 @@ public: | |||||
| connection (nil), | connection (nil), | ||||
| data ([[NSMutableData data] retain]), | data ([[NSMutableData data] retain]), | ||||
| headers (nil), | headers (nil), | ||||
| statusCode (0), | |||||
| initialised (false), | initialised (false), | ||||
| hasFailed (false), | hasFailed (false), | ||||
| hasFinished (false) | hasFinished (false) | ||||
| @@ -202,7 +203,11 @@ public: | |||||
| headers = nil; | headers = nil; | ||||
| if ([response isKindOfClass: [NSHTTPURLResponse class]]) | if ([response isKindOfClass: [NSHTTPURLResponse class]]) | ||||
| headers = [[((NSHTTPURLResponse*) response) allHeaderFields] retain]; | |||||
| { | |||||
| NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response; | |||||
| headers = [[httpResponse allHeaderFields] retain]; | |||||
| statusCode = (int) [httpResponse statusCode]; | |||||
| } | |||||
| } | } | ||||
| void didFailWithError (NSError* error) | void didFailWithError (NSError* error) | ||||
| @@ -251,6 +256,7 @@ public: | |||||
| NSURLConnection* connection; | NSURLConnection* connection; | ||||
| NSMutableData* data; | NSMutableData* data; | ||||
| NSDictionary* headers; | NSDictionary* headers; | ||||
| int statusCode; | |||||
| bool initialised, hasFailed, hasFinished; | bool initialised, hasFailed, hasFinished; | ||||
| private: | private: | ||||
| @@ -318,7 +324,7 @@ public: | |||||
| WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | ||||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | ||||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | ||||
| : address (address_), headers (headers_), postData (postData_), position (0), | |||||
| : statusCode (0), address (address_), headers (headers_), postData (postData_), position (0), | |||||
| finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | ||||
| { | { | ||||
| JUCE_AUTORELEASEPOOL | JUCE_AUTORELEASEPOOL | ||||
| @@ -327,6 +333,8 @@ public: | |||||
| if (responseHeaders != nullptr && connection != nullptr && connection->headers != nil) | if (responseHeaders != nullptr && connection != nullptr && connection->headers != nil) | ||||
| { | { | ||||
| statusCode = connection->statusCode; | |||||
| NSEnumerator* enumerator = [connection->headers keyEnumerator]; | NSEnumerator* enumerator = [connection->headers keyEnumerator]; | ||||
| while (NSString* key = [enumerator nextObject]) | while (NSString* key = [enumerator nextObject]) | ||||
| @@ -380,6 +388,8 @@ public: | |||||
| return true; | return true; | ||||
| } | } | ||||
| int statusCode; | |||||
| private: | private: | ||||
| ScopedPointer<URLConnectionState> connection; | ScopedPointer<URLConnectionState> connection; | ||||
| String address, headers; | String address, headers; | ||||
| @@ -428,14 +438,3 @@ private: | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | ||||
| }; | }; | ||||
| InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, | |||||
| OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||||
| const String& headers, const int timeOutMs, StringPairArray* responseHeaders) | |||||
| { | |||||
| ScopedPointer<WebInputStream> wi (new WebInputStream (address, isPost, postData, | |||||
| progressCallback, progressCallbackContext, | |||||
| headers, timeOutMs, responseHeaders)); | |||||
| return wi->isError() ? nullptr : wi.release(); | |||||
| } | |||||
| @@ -996,6 +996,11 @@ public: | |||||
| ActiveProcess (const StringArray& arguments, int streamFlags) | ActiveProcess (const StringArray& arguments, int streamFlags) | ||||
| : childPID (0), pipeHandle (0), readHandle (0) | : childPID (0), pipeHandle (0), readHandle (0) | ||||
| { | { | ||||
| // Looks like you're trying to launch a non-existent exe or a folder (perhaps on OSX | |||||
| // you're trying to launch the .app folder rather than the actual binary inside it?) | |||||
| jassert ((! arguments[0].containsChar ('/')) | |||||
| || File::getCurrentWorkingDirectory().getChildFile (arguments[0]).existsAsFile()); | |||||
| int pipeHandles[2] = { 0 }; | int pipeHandles[2] = { 0 }; | ||||
| if (pipe (pipeHandles) == 0) | if (pipe (pipeHandles) == 0) | ||||
| @@ -41,41 +41,50 @@ public: | |||||
| WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | ||||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | ||||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | ||||
| : connection (0), request (0), | |||||
| : statusCode (0), connection (0), request (0), | |||||
| address (address_), headers (headers_), postData (postData_), position (0), | address (address_), headers (headers_), postData (postData_), position (0), | ||||
| finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | ||||
| { | { | ||||
| createConnection (progressCallback, progressCallbackContext); | createConnection (progressCallback, progressCallbackContext); | ||||
| if (responseHeaders != nullptr && ! isError()) | |||||
| if (! isError()) | |||||
| { | { | ||||
| DWORD bufferSizeBytes = 4096; | |||||
| for (;;) | |||||
| if (responseHeaders != nullptr) | |||||
| { | { | ||||
| HeapBlock<char> buffer ((size_t) bufferSizeBytes); | |||||
| DWORD bufferSizeBytes = 4096; | |||||
| if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) | |||||
| for (;;) | |||||
| { | { | ||||
| StringArray headersArray; | |||||
| headersArray.addLines (String (reinterpret_cast<const WCHAR*> (buffer.getData()))); | |||||
| HeapBlock<char> buffer ((size_t) bufferSizeBytes); | |||||
| for (int i = 0; i < headersArray.size(); ++i) | |||||
| if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) | |||||
| { | { | ||||
| const String& header = headersArray[i]; | |||||
| const String key (header.upToFirstOccurrenceOf (": ", false, false)); | |||||
| const String value (header.fromFirstOccurrenceOf (": ", false, false)); | |||||
| const String previousValue ((*responseHeaders) [key]); | |||||
| StringArray headersArray; | |||||
| headersArray.addLines (String (reinterpret_cast<const WCHAR*> (buffer.getData()))); | |||||
| for (int i = 0; i < headersArray.size(); ++i) | |||||
| { | |||||
| const String& header = headersArray[i]; | |||||
| const String key (header.upToFirstOccurrenceOf (": ", false, false)); | |||||
| const String value (header.fromFirstOccurrenceOf (": ", false, false)); | |||||
| const String previousValue ((*responseHeaders) [key]); | |||||
| responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); | |||||
| responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); | |||||
| } | |||||
| break; | |||||
| } | } | ||||
| break; | |||||
| if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) | |||||
| break; | |||||
| } | } | ||||
| if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) | |||||
| break; | |||||
| } | } | ||||
| DWORD status = 0; | |||||
| DWORD statusSize = sizeof (status); | |||||
| if (HttpQueryInfo (request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &statusSize, 0)) | |||||
| statusCode = (int) status; | |||||
| } | } | ||||
| } | } | ||||
| @@ -145,6 +154,8 @@ public: | |||||
| return true; | return true; | ||||
| } | } | ||||
| int statusCode; | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| HINTERNET connection, request; | HINTERNET connection, request; | ||||
| @@ -305,17 +316,6 @@ private: | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) | ||||
| }; | }; | ||||
| InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, | |||||
| OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||||
| const String& headers, const int timeOutMs, StringPairArray* responseHeaders) | |||||
| { | |||||
| ScopedPointer <WebInputStream> wi (new WebInputStream (address, isPost, postData, | |||||
| progressCallback, progressCallbackContext, | |||||
| headers, timeOutMs, responseHeaders)); | |||||
| return wi->isError() ? nullptr : wi.release(); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| struct GetAdaptersInfoHelper | struct GetAdaptersInfoHelper | ||||
| @@ -45,72 +45,67 @@ void Logger::outputDebugString (const String& text) | |||||
| #pragma intrinsic (__cpuid) | #pragma intrinsic (__cpuid) | ||||
| #pragma intrinsic (__rdtsc) | #pragma intrinsic (__rdtsc) | ||||
| String SystemStats::getCpuVendor() | |||||
| static void callCPUID (int result[4], int infoType) | |||||
| { | { | ||||
| int info [4]; | |||||
| __cpuid (info, 0); | |||||
| char v [12]; | |||||
| memcpy (v, info + 1, 4); | |||||
| memcpy (v + 4, info + 3, 4); | |||||
| memcpy (v + 8, info + 2, 4); | |||||
| return String (v, 12); | |||||
| __cpuid (result, infoType); | |||||
| } | } | ||||
| #else | #else | ||||
| //============================================================================== | |||||
| // CPU info functions using old fashioned inline asm... | |||||
| static void juce_getCpuVendor (char* const v) | |||||
| static void callCPUID (int result[4], int infoType) | |||||
| { | { | ||||
| int vendor[4] = { 0 }; | |||||
| #if ! JUCE_MINGW | #if ! JUCE_MINGW | ||||
| __try | __try | ||||
| #endif | #endif | ||||
| { | { | ||||
| #if JUCE_GCC | #if JUCE_GCC | ||||
| unsigned int dummy = 0; | |||||
| __asm__ ("cpuid" : "=a" (dummy), "=b" (vendor[0]), "=c" (vendor[2]),"=d" (vendor[1]) : "a" (0)); | |||||
| __asm__ __volatile__ ("cpuid" : "=a" (result[0]), "=b" (result[1]), "=c" (result[2]),"=d" (result[3]) : "a" (infoType)); | |||||
| #else | #else | ||||
| __asm | __asm | ||||
| { | { | ||||
| mov eax, 0 | |||||
| mov esi, result | |||||
| mov eax, infoType | |||||
| xor ecx, ecx | |||||
| cpuid | cpuid | ||||
| mov [vendor], ebx | |||||
| mov [vendor + 4], edx | |||||
| mov [vendor + 8], ecx | |||||
| mov dword ptr [esi + 0], eax | |||||
| mov dword ptr [esi + 4], ebx | |||||
| mov dword ptr [esi + 8], ecx | |||||
| mov dword ptr [esi + 12], edx | |||||
| } | } | ||||
| #endif | #endif | ||||
| } | } | ||||
| #if ! JUCE_MINGW | #if ! JUCE_MINGW | ||||
| __except (EXCEPTION_EXECUTE_HANDLER) | |||||
| { | |||||
| } | |||||
| __except (EXCEPTION_EXECUTE_HANDLER) {} | |||||
| #endif | #endif | ||||
| memcpy (v, vendor, 16); | |||||
| } | } | ||||
| #endif | |||||
| String SystemStats::getCpuVendor() | String SystemStats::getCpuVendor() | ||||
| { | { | ||||
| char v [16]; | |||||
| juce_getCpuVendor (v); | |||||
| return String (v, 16); | |||||
| } | |||||
| #endif | |||||
| int info[4] = { 0 }; | |||||
| callCPUID (info, 0); | |||||
| char v [12]; | |||||
| memcpy (v, info + 1, 4); | |||||
| memcpy (v + 4, info + 3, 4); | |||||
| memcpy (v + 8, info + 2, 4); | |||||
| return String (v, 12); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| void CPUInformation::initialise() noexcept | void CPUInformation::initialise() noexcept | ||||
| { | { | ||||
| hasMMX = IsProcessorFeaturePresent (PF_MMX_INSTRUCTIONS_AVAILABLE) != 0; | |||||
| hasSSE = IsProcessorFeaturePresent (PF_XMMI_INSTRUCTIONS_AVAILABLE) != 0; | |||||
| hasSSE2 = IsProcessorFeaturePresent (PF_XMMI64_INSTRUCTIONS_AVAILABLE) != 0; | |||||
| hasSSE3 = IsProcessorFeaturePresent (13 /*PF_SSE3_INSTRUCTIONS_AVAILABLE*/) != 0; | |||||
| has3DNow = IsProcessorFeaturePresent (7 /*PF_AMD3D_INSTRUCTIONS_AVAILABLE*/) != 0; | |||||
| int info[4] = { 0 }; | |||||
| callCPUID (info, 1); | |||||
| // NB: IsProcessorFeaturePresent doesn't work on XP | |||||
| hasMMX = (info[3] & (1 << 23)) != 0; | |||||
| hasSSE = (info[3] & (1 << 25)) != 0; | |||||
| hasSSE2 = (info[3] & (1 << 26)) != 0; | |||||
| hasSSE3 = (info[2] & (1 << 0)) != 0; | |||||
| has3DNow = (info[1] & (1 << 31)) != 0; | |||||
| SYSTEM_INFO systemInfo; | SYSTEM_INFO systemInfo; | ||||
| GetNativeSystemInfo (&systemInfo); | GetNativeSystemInfo (&systemInfo); | ||||
| @@ -63,6 +63,13 @@ URL::URL (const String& u) : url (u) | |||||
| } | } | ||||
| } | } | ||||
| URL::URL (const String& u, int) : url (u) {} | |||||
| URL URL::createWithoutParsing (const String& u) | |||||
| { | |||||
| return URL (u, 0); | |||||
| } | |||||
| URL::URL (const URL& other) | URL::URL (const URL& other) | ||||
| : url (other.url), | : url (other.url), | ||||
| postData (other.postData), | postData (other.postData), | ||||
| @@ -323,7 +330,8 @@ InputStream* URL::createInputStream (const bool usePostCommand, | |||||
| void* const progressCallbackContext, | void* const progressCallbackContext, | ||||
| String headers, | String headers, | ||||
| const int timeOutMs, | const int timeOutMs, | ||||
| StringPairArray* const responseHeaders) const | |||||
| StringPairArray* const responseHeaders, | |||||
| int* statusCode) const | |||||
| { | { | ||||
| MemoryBlock headersAndPostData; | MemoryBlock headersAndPostData; | ||||
| @@ -336,9 +344,15 @@ InputStream* URL::createInputStream (const bool usePostCommand, | |||||
| if (! headers.endsWithChar ('\n')) | if (! headers.endsWithChar ('\n')) | ||||
| headers << "\r\n"; | headers << "\r\n"; | ||||
| return createNativeStream (toString (! usePostCommand), usePostCommand, headersAndPostData, | |||||
| progressCallback, progressCallbackContext, | |||||
| headers, timeOutMs, responseHeaders); | |||||
| ScopedPointer<WebInputStream> wi (new WebInputStream (toString (! usePostCommand), | |||||
| usePostCommand, headersAndPostData, | |||||
| progressCallback, progressCallbackContext, | |||||
| headers, timeOutMs, responseHeaders)); | |||||
| if (statusCode != nullptr) | |||||
| *statusCode = wi->statusCode; | |||||
| return wi->isError() ? nullptr : wi.release(); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -44,7 +44,11 @@ public: | |||||
| /** Creates an empty URL. */ | /** Creates an empty URL. */ | ||||
| URL(); | URL(); | ||||
| /** Creates a URL from a string. */ | |||||
| /** Creates a URL from a string. | |||||
| This will parse any embedded parameters after a '?' character and store them | |||||
| in the list (see getParameterNames etc). If you don't want this to happen, you | |||||
| can use createWithoutParsing(). | |||||
| */ | |||||
| URL (const String& url); | URL (const String& url); | ||||
| /** Creates a copy of another URL. */ | /** Creates a copy of another URL. */ | ||||
| @@ -243,8 +247,10 @@ public: | |||||
| @param connectionTimeOutMs if 0, this will use whatever default setting the OS chooses. If | @param connectionTimeOutMs if 0, this will use whatever default setting the OS chooses. If | ||||
| a negative number, it will be infinite. Otherwise it specifies a | a negative number, it will be infinite. Otherwise it specifies a | ||||
| time in milliseconds. | time in milliseconds. | ||||
| @param responseHeaders if this is non-zero, all the (key, value) pairs received as headers | |||||
| @param responseHeaders if this is non-null, all the (key, value) pairs received as headers | |||||
| in the response will be stored in this array | in the response will be stored in this array | ||||
| @param statusCode if this is non-null, it will get set to the http status code, if one | |||||
| is known, or 0 if a code isn't available | |||||
| @returns an input stream that the caller must delete, or a null pointer if there was an | @returns an input stream that the caller must delete, or a null pointer if there was an | ||||
| error trying to open it. | error trying to open it. | ||||
| */ | */ | ||||
| @@ -253,7 +259,8 @@ public: | |||||
| void* progressCallbackContext = nullptr, | void* progressCallbackContext = nullptr, | ||||
| String extraHeaders = String(), | String extraHeaders = String(), | ||||
| int connectionTimeOutMs = 0, | int connectionTimeOutMs = 0, | ||||
| StringPairArray* responseHeaders = nullptr) const; | |||||
| StringPairArray* responseHeaders = nullptr, | |||||
| int* statusCode = nullptr) const; | |||||
| //============================================================================== | //============================================================================== | ||||
| @@ -326,18 +333,21 @@ public: | |||||
| */ | */ | ||||
| static String removeEscapeChars (const String& stringToRemoveEscapeCharsFrom); | static String removeEscapeChars (const String& stringToRemoveEscapeCharsFrom); | ||||
| /** Returns a URL without attempting to remove any embedded parameters from the string. | |||||
| This may be necessary if you need to create a request that involves both POST | |||||
| parameters and parameters which are embedded in the URL address itself. | |||||
| */ | |||||
| static URL createWithoutParsing (const String& url); | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| String url, postData; | String url, postData; | ||||
| StringArray parameterNames, parameterValues; | StringArray parameterNames, parameterValues; | ||||
| StringPairArray filesToUpload, mimeTypes; | StringPairArray filesToUpload, mimeTypes; | ||||
| URL (const String&, int); | |||||
| void addParameter (const String&, const String&); | void addParameter (const String&, const String&); | ||||
| static InputStream* createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, | |||||
| OpenStreamProgressCallback* progressCallback, | |||||
| void* progressCallbackContext, const String& headers, | |||||
| const int timeOutMs, StringPairArray* responseHeaders); | |||||
| JUCE_LEAK_DETECTOR (URL) | JUCE_LEAK_DETECTOR (URL) | ||||
| }; | }; | ||||
| @@ -226,7 +226,7 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Sets the string that will be written to the stream when the writeNewLine() | /** Sets the string that will be written to the stream when the writeNewLine() | ||||
| method is called. | method is called. | ||||
| By default this will be set the the value of NewLine::getDefault(). | |||||
| By default this will be set the value of NewLine::getDefault(). | |||||
| */ | */ | ||||
| void setNewLineString (const String& newLineString); | void setNewLineString (const String& newLineString); | ||||
| @@ -36,7 +36,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 3 | #define JUCE_MAJOR_VERSION 3 | ||||
| #define JUCE_MINOR_VERSION 0 | #define JUCE_MINOR_VERSION 0 | ||||
| #define JUCE_BUILDNUMBER 2 | |||||
| #define JUCE_BUILDNUMBER 4 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||
| @@ -38,6 +38,10 @@ | |||||
| one of these is by using RAII in the form of a local ScopedLock object - have a look | one of these is by using RAII in the form of a local ScopedLock object - have a look | ||||
| through the codebase for many examples of how to do this. | through the codebase for many examples of how to do this. | ||||
| In almost all cases you'll want to declare your CriticalSection as a member variable. | |||||
| Occasionally you may want to declare one as a static variable, but in that case the usual | |||||
| C++ static object order-of-construction warnings should be heeded. | |||||
| @see ScopedLock, ScopedTryLock, ScopedUnlock, SpinLock, ReadWriteLock, Thread, InterProcessLock | @see ScopedLock, ScopedTryLock, ScopedUnlock, SpinLock, ReadWriteLock, Thread, InterProcessLock | ||||
| */ | */ | ||||
| class JUCE_API CriticalSection | class JUCE_API CriticalSection | ||||
| @@ -157,21 +161,27 @@ private: | |||||
| /** | /** | ||||
| Automatically locks and unlocks a CriticalSection object. | Automatically locks and unlocks a CriticalSection object. | ||||
| Use one of these as a local variable to provide RAII-based locking of a CriticalSection. | |||||
| You can use a ScopedLock as a local variable to provide RAII-based locking of a CriticalSection. | |||||
| e.g. @code | e.g. @code | ||||
| CriticalSection myCriticalSection; | |||||
| for (;;) | |||||
| struct MyObject | |||||
| { | { | ||||
| const ScopedLock myScopedLock (myCriticalSection); | |||||
| // myCriticalSection is now locked | |||||
| CriticalSection objectLock; | |||||
| // assuming that this example function will be called by multiple threads | |||||
| void foo() | |||||
| { | |||||
| const ScopedLock myScopedLock (objectLock); | |||||
| // objectLock is now locked.. | |||||
| ...do some stuff... | |||||
| ...do some thread-safe work here... | |||||
| // myCriticalSection gets unlocked here. | |||||
| } | |||||
| // ..and objectLock gets unlocked here, as myScopedLock goes out of | |||||
| // scope at the end of the block | |||||
| } | |||||
| }; | |||||
| @endcode | @endcode | ||||
| @see CriticalSection, ScopedUnlock | @see CriticalSection, ScopedUnlock | ||||
| @@ -189,29 +199,29 @@ typedef CriticalSection::ScopedLockType ScopedLock; | |||||
| e.g. @code | e.g. @code | ||||
| CriticalSection myCriticalSection; | |||||
| for (;;) | |||||
| struct MyObject | |||||
| { | { | ||||
| const ScopedLock myScopedLock (myCriticalSection); | |||||
| // myCriticalSection is now locked | |||||
| ... do some stuff with it locked .. | |||||
| CriticalSection objectLock; | |||||
| while (xyz) | |||||
| void foo() | |||||
| { | { | ||||
| ... do some stuff with it locked .. | |||||
| { | |||||
| const ScopedLock myScopedLock (objectLock); | |||||
| const ScopedUnlock unlocker (myCriticalSection); | |||||
| // objectLock is now locked.. | |||||
| // myCriticalSection is now unlocked for the remainder of this block, | |||||
| // and re-locked at the end. | |||||
| { | |||||
| ScopedUnlock myUnlocker (objectLock); | |||||
| ...do some stuff with it unlocked ... | |||||
| } | |||||
| // ..and now unlocked.. | |||||
| } | |||||
| // ..and now locked again.. | |||||
| } | |||||
| // myCriticalSection gets unlocked here. | |||||
| } | |||||
| // ..and finally unlocked. | |||||
| } | |||||
| }; | |||||
| @endcode | @endcode | ||||
| @see CriticalSection, ScopedLock | @see CriticalSection, ScopedLock | ||||
| @@ -225,26 +235,27 @@ typedef CriticalSection::ScopedUnlockType ScopedUnlock; | |||||
| Use one of these as a local variable to control access to a CriticalSection. | Use one of these as a local variable to control access to a CriticalSection. | ||||
| e.g. @code | e.g. @code | ||||
| CriticalSection myCriticalSection; | |||||
| for (;;) | |||||
| struct MyObject | |||||
| { | { | ||||
| const ScopedTryLock myScopedTryLock (myCriticalSection); | |||||
| CriticalSection objectLock; | |||||
| // Unlike using a ScopedLock, this may fail to actually get the lock, so you | |||||
| // should test this with the isLocked() method before doing your thread-unsafe | |||||
| // action.. | |||||
| if (myScopedTryLock.isLocked()) | |||||
| void foo() | |||||
| { | { | ||||
| ...do some stuff... | |||||
| const ScopedTryLock myScopedTryLock (objectLock); | |||||
| // Unlike using a ScopedLock, this may fail to actually get the lock, so you | |||||
| // must call the isLocked() method before making any assumptions.. | |||||
| if (myScopedTryLock.isLocked()) | |||||
| { | |||||
| ...safely do some work... | |||||
| } | |||||
| else | |||||
| { | |||||
| // If we get here, then our attempt at locking failed because another thread had already locked it.. | |||||
| } | |||||
| } | } | ||||
| else | |||||
| { | |||||
| ..our attempt at locking failed because another thread had already locked it.. | |||||
| } | |||||
| // myCriticalSection gets unlocked here (if it was locked) | |||||
| } | |||||
| }; | |||||
| @endcode | @endcode | ||||
| @see CriticalSection::tryEnter, ScopedLock, ScopedUnlock, ScopedReadLock | @see CriticalSection::tryEnter, ScopedLock, ScopedUnlock, ScopedReadLock | ||||
| @@ -57,7 +57,7 @@ public: | |||||
| struct JUCE_API Options | struct JUCE_API Options | ||||
| { | { | ||||
| /** Creates an empty Options structure. | /** Creates an empty Options structure. | ||||
| You'll need to fill-in the data memebers appropriately before using this structure. | |||||
| You'll need to fill-in the data members appropriately before using this structure. | |||||
| */ | */ | ||||
| Options(); | Options(); | ||||
| @@ -73,7 +73,7 @@ private: | |||||
| class ActionMessage; | class ActionMessage; | ||||
| friend class ActionMessage; | friend class ActionMessage; | ||||
| SortedSet <ActionListener*> actionListeners; | |||||
| SortedSet<ActionListener*> actionListeners; | |||||
| CriticalSection actionListenerLock; | CriticalSection actionListenerLock; | ||||
| JUCE_DECLARE_NON_COPYABLE (ActionBroadcaster) | JUCE_DECLARE_NON_COPYABLE (ActionBroadcaster) | ||||
| @@ -28,10 +28,7 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| Receives callbacks to indicate that some kind of event has occurred. | |||||
| Used by various classes, e.g. buttons when they are pressed, to tell listeners | |||||
| about something that's happened. | |||||
| Interface class for delivery of events that are sent by an ActionBroadcaster. | |||||
| @see ActionBroadcaster, ChangeListener | @see ActionBroadcaster, ChangeListener | ||||
| */ | */ | ||||
| @@ -35,7 +35,7 @@ enum NotificationType | |||||
| dontSendNotification = 0, /**< No notification message should be sent. */ | dontSendNotification = 0, /**< No notification message should be sent. */ | ||||
| sendNotification = 1, /**< Requests a notification message, either synchronous or not. */ | sendNotification = 1, /**< Requests a notification message, either synchronous or not. */ | ||||
| sendNotificationSync, /**< Requests a synchronous notification. */ | sendNotificationSync, /**< Requests a synchronous notification. */ | ||||
| sendNotificationAsync, /**< Requests a asynchronous notification. */ | |||||
| sendNotificationAsync, /**< Requests an asynchronous notification. */ | |||||
| }; | }; | ||||
| @@ -151,7 +151,8 @@ public: | |||||
| */ | */ | ||||
| FloatType getAngleToPoint (Point other) const noexcept | FloatType getAngleToPoint (Point other) const noexcept | ||||
| { | { | ||||
| return static_cast<FloatType> (std::atan2 (other.x - x, y - other.y)); | |||||
| return static_cast<FloatType> (std::atan2 (static_cast<FloatType> (other.x - x), | |||||
| static_cast<FloatType> (y - other.y))); | |||||
| } | } | ||||
| /** Returns the point that would be reached by rotating this point clockwise | /** Returns the point that would be reached by rotating this point clockwise | ||||
| @@ -169,8 +170,8 @@ public: | |||||
| */ | */ | ||||
| Point<FloatType> getPointOnCircumference (float radius, float angle) const noexcept | Point<FloatType> getPointOnCircumference (float radius, float angle) const noexcept | ||||
| { | { | ||||
| return Point<FloatType> (static_cast <FloatType> (x + radius * std::sin (angle)), | |||||
| static_cast <FloatType> (y - radius * std::cos (angle))); | |||||
| return Point<FloatType> (static_cast<FloatType> (x + radius * std::sin (angle)), | |||||
| static_cast<FloatType> (y - radius * std::cos (angle))); | |||||
| } | } | ||||
| /** Taking this point to be the centre of an ellipse, this returns a point on its circumference. | /** Taking this point to be the centre of an ellipse, this returns a point on its circumference. | ||||
| @@ -180,8 +181,8 @@ public: | |||||
| */ | */ | ||||
| Point<FloatType> getPointOnCircumference (float radiusX, float radiusY, float angle) const noexcept | Point<FloatType> getPointOnCircumference (float radiusX, float radiusY, float angle) const noexcept | ||||
| { | { | ||||
| return Point<FloatType> (static_cast <FloatType> (x + radiusX * std::sin (angle)), | |||||
| static_cast <FloatType> (y - radiusY * std::cos (angle))); | |||||
| return Point<FloatType> (static_cast<FloatType> (x + radiusX * std::sin (angle)), | |||||
| static_cast<FloatType> (y - radiusY * std::cos (angle))); | |||||
| } | } | ||||
| /** Returns the dot-product of two points (x1 * x2 + y1 * y2). */ | /** Returns the dot-product of two points (x1 * x2 + y1 * y2). */ | ||||
| @@ -204,13 +205,13 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Casts this point to a Point<int> object. */ | /** Casts this point to a Point<int> object. */ | ||||
| Point<int> toInt() const noexcept { return Point<int> (static_cast <int> (x), static_cast<int> (y)); } | |||||
| Point<int> toInt() const noexcept { return Point<int> (static_cast<int> (x), static_cast<int> (y)); } | |||||
| /** Casts this point to a Point<float> object. */ | /** Casts this point to a Point<float> object. */ | ||||
| Point<float> toFloat() const noexcept { return Point<float> (static_cast <float> (x), static_cast<float> (y)); } | |||||
| Point<float> toFloat() const noexcept { return Point<float> (static_cast<float> (x), static_cast<float> (y)); } | |||||
| /** Casts this point to a Point<double> object. */ | /** Casts this point to a Point<double> object. */ | ||||
| Point<double> toDouble() const noexcept { return Point<double> (static_cast <double> (x), static_cast<double> (y)); } | |||||
| Point<double> toDouble() const noexcept { return Point<double> (static_cast<double> (x), static_cast<double> (y)); } | |||||
| /** Returns the point as a string in the form "x, y". */ | /** Returns the point as a string in the form "x, y". */ | ||||
| String toString() const { return String (x) + ", " + String (y); } | String toString() const { return String (x) + ", " + String (y); } | ||||
| @@ -174,6 +174,12 @@ public: | |||||
| /** Changes the rectangle's height */ | /** Changes the rectangle's height */ | ||||
| inline void setHeight (const ValueType newHeight) noexcept { h = newHeight; } | inline void setHeight (const ValueType newHeight) noexcept { h = newHeight; } | ||||
| /** Changes the position of the rectangle's centre (leaving its size unchanged). */ | |||||
| inline void setCentre (const ValueType newCentreX, const ValueType newCentreY) noexcept { pos.x = newCentreX - w / (ValueType) 2; pos.y = newCentreY - h / (ValueType) 2; } | |||||
| /** Changes the position of the rectangle's centre (leaving its size unchanged). */ | |||||
| inline void setCentre (const Point<ValueType> newCentre) noexcept { setCentre (newCentre.x, newCentre.y); } | |||||
| /** Returns a rectangle which has the same size and y-position as this one, but with a different x-position. */ | /** Returns a rectangle which has the same size and y-position as this one, but with a different x-position. */ | ||||
| Rectangle withX (const ValueType newX) const noexcept { return Rectangle (newX, pos.y, w, h); } | Rectangle withX (const ValueType newX) const noexcept { return Rectangle (newX, pos.y, w, h); } | ||||
| @@ -189,6 +195,10 @@ public: | |||||
| /** Returns a rectangle whose size is the same as this one, but whose top-left position is (0, 0). */ | /** Returns a rectangle whose size is the same as this one, but whose top-left position is (0, 0). */ | ||||
| Rectangle withZeroOrigin() const noexcept { return Rectangle (w, h); } | Rectangle withZeroOrigin() const noexcept { return Rectangle (w, h); } | ||||
| /** Returns a rectangle with the same size as this one, but a new centre position. */ | |||||
| Rectangle withCentre (const Point<ValueType> newCentre) const noexcept { return Rectangle (newCentre.x - w / (ValueType) 2, | |||||
| newCentre.y - h / (ValueType) 2, w, h); } | |||||
| /** Returns a rectangle which has the same position and height as this one, but with a different width. */ | /** Returns a rectangle which has the same position and height as this one, but with a different width. */ | ||||
| Rectangle withWidth (ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } | Rectangle withWidth (ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } | ||||
| @@ -31,6 +31,12 @@ ImagePixelData::ImagePixelData (const Image::PixelFormat format, const int w, co | |||||
| ImagePixelData::~ImagePixelData() | ImagePixelData::~ImagePixelData() | ||||
| { | { | ||||
| listeners.call (&Listener::imageDataBeingDeleted, this); | |||||
| } | |||||
| void ImagePixelData::sendDataChangeMessage() | |||||
| { | |||||
| listeners.call (&Listener::imageDataChanged, this); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -69,15 +75,19 @@ public: | |||||
| LowLevelGraphicsContext* createLowLevelContext() override | LowLevelGraphicsContext* createLowLevelContext() override | ||||
| { | { | ||||
| sendDataChangeMessage(); | |||||
| return new LowLevelGraphicsSoftwareRenderer (Image (this)); | return new LowLevelGraphicsSoftwareRenderer (Image (this)); | ||||
| } | } | ||||
| void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override | |||||
| void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override | |||||
| { | { | ||||
| bitmap.data = imageData + x * pixelStride + y * lineStride; | bitmap.data = imageData + x * pixelStride + y * lineStride; | ||||
| bitmap.pixelFormat = pixelFormat; | bitmap.pixelFormat = pixelFormat; | ||||
| bitmap.lineStride = lineStride; | bitmap.lineStride = lineStride; | ||||
| bitmap.pixelStride = pixelStride; | bitmap.pixelStride = pixelStride; | ||||
| if (mode != Image::BitmapData::readOnly) | |||||
| sendDataChangeMessage(); | |||||
| } | } | ||||
| ImagePixelData* clone() override | ImagePixelData* clone() override | ||||
| @@ -146,6 +156,9 @@ public: | |||||
| void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override | void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override | ||||
| { | { | ||||
| image->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode); | image->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode); | ||||
| if (mode != Image::BitmapData::readOnly) | |||||
| sendDataChangeMessage(); | |||||
| } | } | ||||
| ImagePixelData* clone() override | ImagePixelData* clone() override | ||||
| @@ -455,6 +455,19 @@ public: | |||||
| typedef ReferenceCountedObjectPtr<ImagePixelData> Ptr; | typedef ReferenceCountedObjectPtr<ImagePixelData> Ptr; | ||||
| //============================================================================== | |||||
| struct Listener | |||||
| { | |||||
| virtual ~Listener() {} | |||||
| virtual void imageDataChanged (ImagePixelData*) = 0; | |||||
| virtual void imageDataBeingDeleted (ImagePixelData*) = 0; | |||||
| }; | |||||
| ListenerList<Listener> listeners; | |||||
| void sendDataChangeMessage(); | |||||
| private: | private: | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePixelData) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePixelData) | ||||
| }; | }; | ||||
| @@ -52,15 +52,19 @@ public: | |||||
| LowLevelGraphicsContext* createLowLevelContext() override | LowLevelGraphicsContext* createLowLevelContext() override | ||||
| { | { | ||||
| sendDataChangeMessage(); | |||||
| return new CoreGraphicsContext (context, height, 1.0f); | return new CoreGraphicsContext (context, height, 1.0f); | ||||
| } | } | ||||
| void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override | |||||
| void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override | |||||
| { | { | ||||
| bitmap.data = imageData + x * pixelStride + y * lineStride; | bitmap.data = imageData + x * pixelStride + y * lineStride; | ||||
| bitmap.pixelFormat = pixelFormat; | bitmap.pixelFormat = pixelFormat; | ||||
| bitmap.lineStride = lineStride; | bitmap.lineStride = lineStride; | ||||
| bitmap.pixelStride = pixelStride; | bitmap.pixelStride = pixelStride; | ||||
| if (mode != Image::BitmapData::readOnly) | |||||
| sendDataChangeMessage(); | |||||
| } | } | ||||
| ImagePixelData* clone() override | ImagePixelData* clone() override | ||||
| @@ -1230,7 +1230,12 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) | |||||
| Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) | Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) | ||||
| { | { | ||||
| #if JUCE_CORETEXT_AVAILABLE | |||||
| return new OSXTypeface (data, dataSize); | return new OSXTypeface (data, dataSize); | ||||
| #else | |||||
| jassertfalse; // You need CoreText enabled to use this feature! | |||||
| return nullptr; | |||||
| #endif | |||||
| } | } | ||||
| void Typeface::scanFolderForFonts (const File&) | void Typeface::scanFolderForFonts (const File&) | ||||
| @@ -290,7 +290,7 @@ public: | |||||
| static ApplicationCommandTarget* findDefaultComponentTarget(); | static ApplicationCommandTarget* findDefaultComponentTarget(); | ||||
| /** Examines this component and all its parents in turn, looking for the first one | /** Examines this component and all its parents in turn, looking for the first one | ||||
| which is a ApplicationCommandTarget. | |||||
| which is an ApplicationCommandTarget. | |||||
| Returns the first ApplicationCommandTarget that it finds, or nullptr if none of them | Returns the first ApplicationCommandTarget that it finds, or nullptr if none of them | ||||
| implement that class. | implement that class. | ||||
| @@ -29,7 +29,7 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| Manages and edits a list of keypresses, which it uses to invoke the appropriate | Manages and edits a list of keypresses, which it uses to invoke the appropriate | ||||
| command in a ApplicationCommandManager. | |||||
| command in an ApplicationCommandManager. | |||||
| Normally, you won't actually create a KeyPressMappingSet directly, because | Normally, you won't actually create a KeyPressMappingSet directly, because | ||||
| each ApplicationCommandManager contains its own KeyPressMappingSet, so typically | each ApplicationCommandManager contains its own KeyPressMappingSet, so typically | ||||
| @@ -326,12 +326,12 @@ public: | |||||
| void getVisibleArea (RectangleList<int>& result, bool includeSiblings) const; | void getVisibleArea (RectangleList<int>& result, bool includeSiblings) const; | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns this component's x coordinate relative the the screen's top-left origin. | |||||
| /** Returns this component's x coordinate relative the screen's top-left origin. | |||||
| @see getX, localPointToGlobal | @see getX, localPointToGlobal | ||||
| */ | */ | ||||
| int getScreenX() const; | int getScreenX() const; | ||||
| /** Returns this component's y coordinate relative the the screen's top-left origin. | |||||
| /** Returns this component's y coordinate relative the screen's top-left origin. | |||||
| @see getY, localPointToGlobal | @see getY, localPointToGlobal | ||||
| */ | */ | ||||
| int getScreenY() const; | int getScreenY() const; | ||||
| @@ -104,6 +104,9 @@ LookAndFeel_V2::LookAndFeel_V2() | |||||
| ComboBox::backgroundColourId, 0xffffffff, | ComboBox::backgroundColourId, 0xffffffff, | ||||
| ComboBox::arrowColourId, 0x99000000, | ComboBox::arrowColourId, 0x99000000, | ||||
| PropertyComponent::backgroundColourId, 0x66ffffff, | |||||
| PropertyComponent::labelTextColourId, 0xff000000, | |||||
| TextPropertyComponent::backgroundColourId, 0xffffffff, | TextPropertyComponent::backgroundColourId, 0xffffffff, | ||||
| TextPropertyComponent::textColourId, 0xff000000, | TextPropertyComponent::textColourId, 0xff000000, | ||||
| TextPropertyComponent::outlineColourId, standardOutlineColour, | TextPropertyComponent::outlineColourId, standardOutlineColour, | ||||
| @@ -1456,6 +1459,8 @@ Label* LookAndFeel_V2::createSliderTextBox (Slider& slider) | |||||
| l->setColour (TextEditor::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); | l->setColour (TextEditor::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); | ||||
| l->setColour (TextEditor::highlightColourId, slider.findColour (Slider::textBoxHighlightColourId)); | |||||
| return l; | return l; | ||||
| } | } | ||||
| @@ -2302,20 +2307,16 @@ void LookAndFeel_V2::drawPropertyPanelSectionHeader (Graphics& g, const String& | |||||
| g.drawText (name, textX, 0, width - textX - 4, height, Justification::centredLeft, true); | g.drawText (name, textX, 0, width - textX - 4, height, Justification::centredLeft, true); | ||||
| } | } | ||||
| void LookAndFeel_V2::drawPropertyComponentBackground (Graphics& g, int width, int height, | |||||
| PropertyComponent&) | |||||
| void LookAndFeel_V2::drawPropertyComponentBackground (Graphics& g, int width, int height, PropertyComponent& component) | |||||
| { | { | ||||
| g.setColour (Colour (0x66ffffff)); | |||||
| g.setColour (component.findColour (PropertyComponent::backgroundColourId)); | |||||
| g.fillRect (0, 0, width, height - 1); | g.fillRect (0, 0, width, height - 1); | ||||
| } | } | ||||
| void LookAndFeel_V2::drawPropertyComponentLabel (Graphics& g, int, int height, | |||||
| PropertyComponent& component) | |||||
| void LookAndFeel_V2::drawPropertyComponentLabel (Graphics& g, int, int height, PropertyComponent& component) | |||||
| { | { | ||||
| g.setColour (Colours::black); | |||||
| if (! component.isEnabled()) | |||||
| g.setOpacity (0.6f); | |||||
| g.setColour (component.findColour (PropertyComponent::labelTextColourId) | |||||
| .withMultipliedAlpha (component.isEnabled() ? 1.0f : 0.6f)); | |||||
| g.setFont (jmin (height, 24) * 0.65f); | g.setFont (jmin (height, 24) * 0.65f); | ||||
| @@ -617,15 +617,19 @@ public: | |||||
| LowLevelGraphicsContext* createLowLevelContext() override | LowLevelGraphicsContext* createLowLevelContext() override | ||||
| { | { | ||||
| sendDataChangeMessage(); | |||||
| return new LowLevelGraphicsSoftwareRenderer (Image (this)); | return new LowLevelGraphicsSoftwareRenderer (Image (this)); | ||||
| } | } | ||||
| void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override | |||||
| void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override | |||||
| { | { | ||||
| bitmap.data = imageData + x * pixelStride + y * lineStride; | bitmap.data = imageData + x * pixelStride + y * lineStride; | ||||
| bitmap.pixelFormat = pixelFormat; | bitmap.pixelFormat = pixelFormat; | ||||
| bitmap.lineStride = lineStride; | bitmap.lineStride = lineStride; | ||||
| bitmap.pixelStride = pixelStride; | bitmap.pixelStride = pixelStride; | ||||
| if (mode != Image::BitmapData::readOnly) | |||||
| sendDataChangeMessage(); | |||||
| } | } | ||||
| ImagePixelData* clone() override | ImagePixelData* clone() override | ||||
| @@ -328,15 +328,19 @@ public: | |||||
| LowLevelGraphicsContext* createLowLevelContext() override | LowLevelGraphicsContext* createLowLevelContext() override | ||||
| { | { | ||||
| sendDataChangeMessage(); | |||||
| return new LowLevelGraphicsSoftwareRenderer (Image (this)); | return new LowLevelGraphicsSoftwareRenderer (Image (this)); | ||||
| } | } | ||||
| void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) override | |||||
| void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override | |||||
| { | { | ||||
| bitmap.data = imageData + x * pixelStride + y * lineStride; | bitmap.data = imageData + x * pixelStride + y * lineStride; | ||||
| bitmap.pixelFormat = pixelFormat; | bitmap.pixelFormat = pixelFormat; | ||||
| bitmap.lineStride = lineStride; | bitmap.lineStride = lineStride; | ||||
| bitmap.pixelStride = pixelStride; | bitmap.pixelStride = pixelStride; | ||||
| if (mode != Image::BitmapData::readOnly) | |||||
| sendDataChangeMessage(); | |||||
| } | } | ||||
| ImagePixelData* clone() override | ImagePixelData* clone() override | ||||
| @@ -3177,10 +3181,22 @@ void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDis | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| static BOOL CALLBACK enumMonitorsProc (HMONITOR, HDC, LPRECT r, LPARAM userInfo) | |||||
| struct MonitorInfo | |||||
| { | |||||
| MonitorInfo (Rectangle<int> rect, bool main) noexcept : isMain (main), bounds (rect) {} | |||||
| Rectangle<int> bounds; | |||||
| bool isMain; | |||||
| }; | |||||
| static BOOL CALLBACK enumMonitorsProc (HMONITOR hm, HDC, LPRECT r, LPARAM userInfo) | |||||
| { | { | ||||
| Array <Rectangle<int> >* const monitorCoords = (Array <Rectangle<int> >*) userInfo; | |||||
| monitorCoords->add (rectangleFromRECT (*r)); | |||||
| MONITORINFO info = { 0 }; | |||||
| info.cbSize = sizeof (info); | |||||
| GetMonitorInfo (hm, &info); | |||||
| const bool isMain = (info.dwFlags & 1 /* MONITORINFOF_PRIMARY */) != 0; | |||||
| ((Array<MonitorInfo>*) userInfo)->add (MonitorInfo (rectangleFromRECT (*r), isMain)); | |||||
| return TRUE; | return TRUE; | ||||
| } | } | ||||
| @@ -3188,17 +3204,17 @@ void Desktop::Displays::findDisplays (float masterScale) | |||||
| { | { | ||||
| setDPIAwareness(); | setDPIAwareness(); | ||||
| Array <Rectangle<int> > monitors; | |||||
| Array<MonitorInfo> monitors; | |||||
| EnumDisplayMonitors (0, 0, &enumMonitorsProc, (LPARAM) &monitors); | EnumDisplayMonitors (0, 0, &enumMonitorsProc, (LPARAM) &monitors); | ||||
| if (monitors.size() == 0) | |||||
| monitors.add (MonitorInfo (rectangleFromRECT (getWindowRect (GetDesktopWindow())), true)); | |||||
| // make sure the first in the list is the main monitor | // make sure the first in the list is the main monitor | ||||
| for (int i = 1; i < monitors.size(); ++i) | for (int i = 1; i < monitors.size(); ++i) | ||||
| if (monitors.getReference(i).getPosition().isOrigin()) | |||||
| if (monitors.getReference(i).isMain) | |||||
| monitors.swap (i, 0); | monitors.swap (i, 0); | ||||
| if (monitors.size() == 0) | |||||
| monitors.add (rectangleFromRECT (getWindowRect (GetDesktopWindow()))); | |||||
| RECT workArea; | RECT workArea; | ||||
| SystemParametersInfo (SPI_GETWORKAREA, 0, &workArea, 0); | SystemParametersInfo (SPI_GETWORKAREA, 0, &workArea, 0); | ||||
| @@ -3207,12 +3223,12 @@ void Desktop::Displays::findDisplays (float masterScale) | |||||
| for (int i = 0; i < monitors.size(); ++i) | for (int i = 0; i < monitors.size(); ++i) | ||||
| { | { | ||||
| Display d; | Display d; | ||||
| d.userArea = d.totalArea = monitors.getReference(i) / masterScale; | |||||
| d.isMain = (i == 0); | |||||
| d.scale = masterScale; | |||||
| d.dpi = dpi; | |||||
| d.userArea = d.totalArea = monitors.getReference(i).bounds / masterScale; | |||||
| d.isMain = monitors.getReference(i).isMain; | |||||
| d.scale = masterScale; | |||||
| d.dpi = dpi; | |||||
| if (i == 0) | |||||
| if (d.isMain) | |||||
| d.userArea = d.userArea.getIntersection (rectangleFromRECT (workArea) / masterScale); | d.userArea = d.userArea.getIntersection (rectangleFromRECT (workArea) / masterScale); | ||||
| displays.add (d); | displays.add (d); | ||||
| @@ -102,6 +102,22 @@ public: | |||||
| /** By default, this just repaints the component. */ | /** By default, this just repaints the component. */ | ||||
| void enablementChanged() override; | void enablementChanged() override; | ||||
| //============================================================================== | |||||
| /** A set of colour IDs to use to change the colour of various aspects of the combo box. | |||||
| These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() | |||||
| methods. | |||||
| To change the colours of the menu that pops up | |||||
| @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour | |||||
| */ | |||||
| enum ColourIds | |||||
| { | |||||
| backgroundColourId = 0x1008300, /**< The background colour to fill the component with. */ | |||||
| labelTextColourId = 0x1008301, /**< The colour for the property's label text. */ | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** This abstract base class is implemented by LookAndFeel classes. */ | /** This abstract base class is implemented by LookAndFeel classes. */ | ||||
| struct JUCE_API LookAndFeelMethods | struct JUCE_API LookAndFeelMethods | ||||
| @@ -375,7 +375,7 @@ public: | |||||
| const double delta = (button == incButton) ? interval : -interval; | const double delta = (button == incButton) ? interval : -interval; | ||||
| DragInProgress drag (*this); | DragInProgress drag (*this); | ||||
| setValue (owner.snapValue (getValue() + delta, false), sendNotificationSync); | |||||
| setValue (owner.snapValue (getValue() + delta, notDragging), sendNotificationSync); | |||||
| } | } | ||||
| } | } | ||||
| @@ -394,7 +394,7 @@ public: | |||||
| void labelTextChanged (Label* label) override | void labelTextChanged (Label* label) override | ||||
| { | { | ||||
| const double newValue = owner.snapValue (owner.getValueFromText (label->getText()), false); | |||||
| const double newValue = owner.snapValue (owner.getValueFromText (label->getText()), notDragging); | |||||
| if (newValue != (double) currentValue.getValue()) | if (newValue != (double) currentValue.getValue()) | ||||
| { | { | ||||
| @@ -873,6 +873,8 @@ public: | |||||
| && ! ((style == LinearBar || style == LinearBarVertical) | && ! ((style == LinearBar || style == LinearBarVertical) | ||||
| && e.mouseWasClicked() && valueBox != nullptr && valueBox->isEditable())) | && e.mouseWasClicked() && valueBox != nullptr && valueBox->isEditable())) | ||||
| { | { | ||||
| DragMode dragMode = notDragging; | |||||
| if (style == Rotary) | if (style == Rotary) | ||||
| { | { | ||||
| handleRotaryDrag (e); | handleRotaryDrag (e); | ||||
| @@ -889,21 +891,27 @@ public: | |||||
| } | } | ||||
| if (isAbsoluteDragMode (e.mods) || (maximum - minimum) / sliderRegionSize < interval) | if (isAbsoluteDragMode (e.mods) || (maximum - minimum) / sliderRegionSize < interval) | ||||
| { | |||||
| dragMode = notDragging; | |||||
| handleAbsoluteDrag (e); | handleAbsoluteDrag (e); | ||||
| } | |||||
| else | else | ||||
| { | |||||
| dragMode = velocityDrag; | |||||
| handleVelocityDrag (e); | handleVelocityDrag (e); | ||||
| } | |||||
| } | } | ||||
| valueWhenLastDragged = jlimit (minimum, maximum, valueWhenLastDragged); | valueWhenLastDragged = jlimit (minimum, maximum, valueWhenLastDragged); | ||||
| if (sliderBeingDragged == 0) | if (sliderBeingDragged == 0) | ||||
| { | { | ||||
| setValue (owner.snapValue (valueWhenLastDragged, true), | |||||
| setValue (owner.snapValue (valueWhenLastDragged, dragMode), | |||||
| sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationSync); | sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationSync); | ||||
| } | } | ||||
| else if (sliderBeingDragged == 1) | else if (sliderBeingDragged == 1) | ||||
| { | { | ||||
| setMinValue (owner.snapValue (valueWhenLastDragged, true), | |||||
| setMinValue (owner.snapValue (valueWhenLastDragged, dragMode), | |||||
| sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationAsync, true); | sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationAsync, true); | ||||
| if (e.mods.isShiftDown()) | if (e.mods.isShiftDown()) | ||||
| @@ -913,7 +921,7 @@ public: | |||||
| } | } | ||||
| else if (sliderBeingDragged == 2) | else if (sliderBeingDragged == 2) | ||||
| { | { | ||||
| setMaxValue (owner.snapValue (valueWhenLastDragged, true), | |||||
| setMaxValue (owner.snapValue (valueWhenLastDragged, dragMode), | |||||
| sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationAsync, true); | sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationAsync, true); | ||||
| if (e.mods.isShiftDown()) | if (e.mods.isShiftDown()) | ||||
| @@ -994,7 +1002,7 @@ public: | |||||
| delta = -delta; | delta = -delta; | ||||
| DragInProgress drag (*this); | DragInProgress drag (*this); | ||||
| setValue (owner.snapValue (value + delta, false), sendNotificationSync); | |||||
| setValue (owner.snapValue (value + delta, notDragging), sendNotificationSync); | |||||
| } | } | ||||
| return true; | return true; | ||||
| @@ -1534,7 +1542,7 @@ double Slider::valueToProportionOfLength (double value) | |||||
| return skew == 1.0 ? n : pow (n, skew); | return skew == 1.0 ? n : pow (n, skew); | ||||
| } | } | ||||
| double Slider::snapValue (double attemptedValue, const bool) | |||||
| double Slider::snapValue (double attemptedValue, DragMode) | |||||
| { | { | ||||
| return attemptedValue; | return attemptedValue; | ||||
| } | } | ||||
| @@ -97,6 +97,16 @@ public: | |||||
| TextBoxBelow /**< Puts the text box below the slider, horizontally centred. */ | TextBoxBelow /**< Puts the text box below the slider, horizontally centred. */ | ||||
| }; | }; | ||||
| /** Describes the type of mouse-dragging that is happening when a value is being changed. | |||||
| @see snapValue | |||||
| */ | |||||
| enum DragMode | |||||
| { | |||||
| notDragging, /**< Dragging is not active. */ | |||||
| absoluteDrag, /**< The dragging corresponds directly to the value that is displayed. */ | |||||
| velocityDrag /**< The dragging value change is relative to the velocity of the mouse mouvement. */ | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Creates a slider. | /** Creates a slider. | ||||
| When created, you can set up the slider's style and range with setSliderStyle(), setRange(), etc. | When created, you can set up the slider's style and range with setSliderStyle(), setRange(), etc. | ||||
| @@ -157,7 +167,7 @@ public: | |||||
| int getMouseDragSensitivity() const noexcept; | int getMouseDragSensitivity() const noexcept; | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Changes the way the the mouse is used when dragging the slider. | |||||
| /** Changes the way the mouse is used when dragging the slider. | |||||
| If true, this will turn on velocity-sensitive dragging, so that | If true, this will turn on velocity-sensitive dragging, so that | ||||
| the faster the mouse moves, the bigger the movement to the slider. This | the faster the mouse moves, the bigger the movement to the slider. This | ||||
| @@ -720,12 +730,13 @@ public: | |||||
| a given position, and allows a subclass to sanity-check this value, possibly | a given position, and allows a subclass to sanity-check this value, possibly | ||||
| returning a different value to use instead. | returning a different value to use instead. | ||||
| @param attemptedValue the value the user is trying to enter | |||||
| @param userIsDragging true if the user is dragging with the mouse; false if | |||||
| they are entering the value using the text box | |||||
| @returns the value to use instead | |||||
| @param attemptedValue the value the user is trying to enter | |||||
| @param dragMode indicates whether the user is dragging with | |||||
| the mouse; notDragging if they are entering the value | |||||
| using the text box or other non-dragging interaction | |||||
| @returns the value to use instead | |||||
| */ | */ | ||||
| virtual double snapValue (double attemptedValue, bool userIsDragging); | |||||
| virtual double snapValue (double attemptedValue, DragMode dragMode); | |||||
| //============================================================================== | //============================================================================== | ||||
| @@ -874,6 +885,7 @@ private: | |||||
| JUCE_DEPRECATED (void setMaxValue (double, bool)); | JUCE_DEPRECATED (void setMaxValue (double, bool)); | ||||
| JUCE_DEPRECATED (void setMinAndMaxValues (double, double, bool, bool)); | JUCE_DEPRECATED (void setMinAndMaxValues (double, double, bool, bool)); | ||||
| JUCE_DEPRECATED (void setMinAndMaxValues (double, double, bool)); | JUCE_DEPRECATED (void setMinAndMaxValues (double, double, bool)); | ||||
| virtual void snapValue (double, bool) {} | |||||
| #endif | #endif | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Slider) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Slider) | ||||
| @@ -560,7 +560,7 @@ Viewport* TreeView::getViewport() const noexcept | |||||
| void TreeView::clearSelectedItems() | void TreeView::clearSelectedItems() | ||||
| { | { | ||||
| if (rootItem != nullptr) | if (rootItem != nullptr) | ||||
| rootItem->deselectAllRecursively(); | |||||
| rootItem->deselectAllRecursively (nullptr); | |||||
| } | } | ||||
| int TreeView::getNumSelectedItems (int maximumDepthToSearchTo) const noexcept | int TreeView::getNumSelectedItems (int maximumDepthToSearchTo) const noexcept | ||||
| @@ -1299,12 +1299,13 @@ bool TreeViewItem::isSelected() const noexcept | |||||
| return selected; | return selected; | ||||
| } | } | ||||
| void TreeViewItem::deselectAllRecursively() | |||||
| void TreeViewItem::deselectAllRecursively (TreeViewItem* itemToIgnore) | |||||
| { | { | ||||
| setSelected (false, false); | |||||
| if (this != itemToIgnore) | |||||
| setSelected (false, false); | |||||
| for (int i = 0; i < subItems.size(); ++i) | for (int i = 0; i < subItems.size(); ++i) | ||||
| subItems.getUnchecked(i)->deselectAllRecursively(); | |||||
| subItems.getUnchecked(i)->deselectAllRecursively (itemToIgnore); | |||||
| } | } | ||||
| void TreeViewItem::setSelected (const bool shouldBeSelected, | void TreeViewItem::setSelected (const bool shouldBeSelected, | ||||
| @@ -1315,7 +1316,7 @@ void TreeViewItem::setSelected (const bool shouldBeSelected, | |||||
| return; | return; | ||||
| if (deselectOtherItemsFirst) | if (deselectOtherItemsFirst) | ||||
| getTopLevelItem()->deselectAllRecursively(); | |||||
| getTopLevelItem()->deselectAllRecursively (this); | |||||
| if (shouldBeSelected != selected) | if (shouldBeSelected != selected) | ||||
| { | { | ||||
| @@ -557,7 +557,7 @@ private: | |||||
| TreeViewItem* getDeepestOpenParentItem() noexcept; | TreeViewItem* getDeepestOpenParentItem() noexcept; | ||||
| int getNumRows() const noexcept; | int getNumRows() const noexcept; | ||||
| TreeViewItem* getItemOnRow (int index) noexcept; | TreeViewItem* getItemOnRow (int index) noexcept; | ||||
| void deselectAllRecursively(); | |||||
| void deselectAllRecursively (TreeViewItem* itemToIgnore); | |||||
| int countSelectedItemsRecursively (int depth) const noexcept; | int countSelectedItemsRecursively (int depth) const noexcept; | ||||
| TreeViewItem* getSelectedItemWithIndex (int index) noexcept; | TreeViewItem* getSelectedItemWithIndex (int index) noexcept; | ||||
| TreeViewItem* getNextVisibleItem (bool recurse) const noexcept; | TreeViewItem* getNextVisibleItem (bool recurse) const noexcept; | ||||
| @@ -48,8 +48,6 @@ CallOutBox::~CallOutBox() | |||||
| { | { | ||||
| } | } | ||||
| enum { callOutBoxDismissCommandId = 0x4f83a04b }; | |||||
| //============================================================================== | //============================================================================== | ||||
| class CallOutBoxCallback : public ModalComponentManager::Callback, | class CallOutBoxCallback : public ModalComponentManager::Callback, | ||||
| private Timer | private Timer | ||||
| @@ -68,7 +66,7 @@ public: | |||||
| void timerCallback() override | void timerCallback() override | ||||
| { | { | ||||
| if (! Process::isForegroundProcess()) | if (! Process::isForegroundProcess()) | ||||
| callout.postCommandMessage (callOutBoxDismissCommandId); | |||||
| callout.dismiss(); | |||||
| } | } | ||||
| ScopedPointer<Component> content; | ScopedPointer<Component> content; | ||||
| @@ -127,7 +125,7 @@ void CallOutBox::inputAttemptWhenModal() | |||||
| // if you click on the area that originally popped-up the callout, you expect it | // if you click on the area that originally popped-up the callout, you expect it | ||||
| // to get rid of the box, but deleting the box here allows the click to pass through and | // to get rid of the box, but deleting the box here allows the click to pass through and | ||||
| // probably re-trigger it, so we need to dismiss the box asynchronously to consume the click.. | // probably re-trigger it, so we need to dismiss the box asynchronously to consume the click.. | ||||
| postCommandMessage (callOutBoxDismissCommandId); | |||||
| dismiss(); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -136,6 +134,8 @@ void CallOutBox::inputAttemptWhenModal() | |||||
| } | } | ||||
| } | } | ||||
| enum { callOutBoxDismissCommandId = 0x4f83a04b }; | |||||
| void CallOutBox::handleCommandMessage (int commandId) | void CallOutBox::handleCommandMessage (int commandId) | ||||
| { | { | ||||
| Component::handleCommandMessage (commandId); | Component::handleCommandMessage (commandId); | ||||
| @@ -147,6 +147,11 @@ void CallOutBox::handleCommandMessage (int commandId) | |||||
| } | } | ||||
| } | } | ||||
| void CallOutBox::dismiss() | |||||
| { | |||||
| postCommandMessage (callOutBoxDismissCommandId); | |||||
| } | |||||
| bool CallOutBox::keyPressed (const KeyPress& key) | bool CallOutBox::keyPressed (const KeyPress& key) | ||||
| { | { | ||||
| if (key.isKeyCode (KeyPress::escapeKey)) | if (key.isKeyCode (KeyPress::escapeKey)) | ||||
| @@ -118,6 +118,11 @@ public: | |||||
| const Rectangle<int>& areaToPointTo, | const Rectangle<int>& areaToPointTo, | ||||
| Component* parentComponent); | Component* parentComponent); | ||||
| /** Posts a message which will dismiss the callout box asynchronously. | |||||
| NB: it's safe to call this method from any thread. | |||||
| */ | |||||
| void dismiss(); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** This abstract base class is implemented by LookAndFeel classes. */ | /** This abstract base class is implemented by LookAndFeel classes. */ | ||||
| struct JUCE_API LookAndFeelMethods | struct JUCE_API LookAndFeelMethods | ||||
| @@ -102,7 +102,7 @@ public: | |||||
| /** Returns the raw handle to whatever kind of window is being used. | /** Returns the raw handle to whatever kind of window is being used. | ||||
| On windows, this is probably a HWND, on the mac, it's likely to be a WindowRef, | On windows, this is probably a HWND, on the mac, it's likely to be a WindowRef, | ||||
| but rememeber there's no guarantees what you'll get back. | |||||
| but remember there's no guarantees what you'll get back. | |||||
| */ | */ | ||||
| virtual void* getNativeHandle() const = 0; | virtual void* getNativeHandle() const = 0; | ||||
| @@ -25,12 +25,13 @@ | |||||
| ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title, | ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title, | ||||
| const bool hasProgressBar, | const bool hasProgressBar, | ||||
| const bool hasCancelButton, | const bool hasCancelButton, | ||||
| const int timeOutMsWhenCancelling_, | |||||
| const int cancellingTimeOutMs, | |||||
| const String& cancelButtonText, | const String& cancelButtonText, | ||||
| Component* componentToCentreAround) | Component* componentToCentreAround) | ||||
| : Thread ("Juce Progress Window"), | |||||
| progress (0.0), | |||||
| timeOutMsWhenCancelling (timeOutMsWhenCancelling_) | |||||
| : Thread ("ThreadWithProgressWindow"), | |||||
| progress (0.0), | |||||
| timeOutMsWhenCancelling (cancellingTimeOutMs), | |||||
| wasCancelledByUser (false) | |||||
| { | { | ||||
| alertWindow = LookAndFeel::getDefaultLookAndFeel() | alertWindow = LookAndFeel::getDefaultLookAndFeel() | ||||
| .createAlertWindow (title, String(), | .createAlertWindow (title, String(), | ||||
| @@ -52,8 +53,7 @@ ThreadWithProgressWindow::~ThreadWithProgressWindow() | |||||
| stopThread (timeOutMsWhenCancelling); | stopThread (timeOutMsWhenCancelling); | ||||
| } | } | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| bool ThreadWithProgressWindow::runThread (const int priority) | |||||
| void ThreadWithProgressWindow::launchThread (int priority) | |||||
| { | { | ||||
| jassert (MessageManager::getInstance()->isThisTheMessageThread()); | jassert (MessageManager::getInstance()->isThisTheMessageThread()); | ||||
| @@ -65,15 +65,8 @@ bool ThreadWithProgressWindow::runThread (const int priority) | |||||
| alertWindow->setMessage (message); | alertWindow->setMessage (message); | ||||
| } | } | ||||
| const bool finishedNaturally = alertWindow->runModalLoop() != 0; | |||||
| stopThread (timeOutMsWhenCancelling); | |||||
| alertWindow->setVisible (false); | |||||
| return finishedNaturally; | |||||
| alertWindow->enterModalState(); | |||||
| } | } | ||||
| #endif | |||||
| void ThreadWithProgressWindow::setProgress (const double newProgress) | void ThreadWithProgressWindow::setProgress (const double newProgress) | ||||
| { | { | ||||
| @@ -88,15 +81,34 @@ void ThreadWithProgressWindow::setStatusMessage (const String& newStatusMessage) | |||||
| void ThreadWithProgressWindow::timerCallback() | void ThreadWithProgressWindow::timerCallback() | ||||
| { | { | ||||
| if (! isThreadRunning()) | |||||
| bool threadStillRunning = isThreadRunning(); | |||||
| if (! (threadStillRunning && alertWindow->isCurrentlyModal())) | |||||
| { | { | ||||
| // thread has finished normally.. | |||||
| stopTimer(); | |||||
| stopThread (timeOutMsWhenCancelling); | |||||
| alertWindow->exitModalState (1); | alertWindow->exitModalState (1); | ||||
| alertWindow->setVisible (false); | alertWindow->setVisible (false); | ||||
| wasCancelledByUser = threadStillRunning; | |||||
| threadComplete (threadStillRunning); | |||||
| return; // (this may be deleted now) | |||||
| } | } | ||||
| else | |||||
| { | |||||
| const ScopedLock sl (messageLock); | |||||
| alertWindow->setMessage (message); | |||||
| } | |||||
| const ScopedLock sl (messageLock); | |||||
| alertWindow->setMessage (message); | |||||
| } | |||||
| void ThreadWithProgressWindow::threadComplete (bool) {} | |||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| bool ThreadWithProgressWindow::runThread (const int priority) | |||||
| { | |||||
| launchThread (priority); | |||||
| while (isTimerRunning()) | |||||
| MessageManager::getInstance()->runDispatchLoopUntil (5); | |||||
| return ! wasCancelledByUser; | |||||
| } | } | ||||
| #endif | |||||
| @@ -111,6 +111,7 @@ public: | |||||
| ~ThreadWithProgressWindow(); | ~ThreadWithProgressWindow(); | ||||
| //============================================================================== | //============================================================================== | ||||
| #if JUCE_MODAL_LOOPS_PERMITTED | |||||
| /** Starts the thread and waits for it to finish. | /** Starts the thread and waits for it to finish. | ||||
| This will start the thread, make the dialog box appear, and wait until either | This will start the thread, make the dialog box appear, and wait until either | ||||
| @@ -123,6 +124,18 @@ public: | |||||
| @returns true if the thread finished normally; false if the user pressed cancel | @returns true if the thread finished normally; false if the user pressed cancel | ||||
| */ | */ | ||||
| bool runThread (int threadPriority = 5); | bool runThread (int threadPriority = 5); | ||||
| #endif | |||||
| /** Starts the thread and returns. | |||||
| This will start the thread and make the dialog box appear in a modal state. When | |||||
| the thread finishes normally, or the cancel button is pressed, the window will be | |||||
| hidden and the threadComplete() method will be called. | |||||
| @param threadPriority the priority to use when starting the thread - see | |||||
| Thread::startThread() for values | |||||
| */ | |||||
| void launchThread (int threadPriority = 5); | |||||
| /** The thread should call this periodically to update the position of the progress bar. | /** The thread should call this periodically to update the position of the progress bar. | ||||
| @@ -137,15 +150,22 @@ public: | |||||
| /** Returns the AlertWindow that is being used. */ | /** Returns the AlertWindow that is being used. */ | ||||
| AlertWindow* getAlertWindow() const noexcept { return alertWindow; } | AlertWindow* getAlertWindow() const noexcept { return alertWindow; } | ||||
| //============================================================================== | |||||
| /** This method is called (on the message thread) when the operation has finished. | |||||
| You may choose to use this callback to delete the ThreadWithProgressWindow object. | |||||
| */ | |||||
| virtual void threadComplete (bool userPressedCancel); | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| void timerCallback() override; | void timerCallback() override; | ||||
| double progress; | double progress; | ||||
| ScopedPointer <AlertWindow> alertWindow; | |||||
| ScopedPointer<AlertWindow> alertWindow; | |||||
| String message; | String message; | ||||
| CriticalSection messageLock; | CriticalSection messageLock; | ||||
| const int timeOutMsWhenCancelling; | const int timeOutMsWhenCancelling; | ||||
| bool wasCancelledByUser; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadWithProgressWindow) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadWithProgressWindow) | ||||
| }; | }; | ||||