From 8e6f506803de9ba2cccfc4500764ddf9c1df95d0 Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 27 May 2015 10:00:50 +0100 Subject: [PATCH 01/11] Fixed an issue with CFString releasing in OSX midi when devices fail to open. --- .../native/juce_mac_CoreMidi.cpp | 70 ++++++++++--------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp b/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp index 5f8b3d5a67..dee763316c 100644 --- a/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp +++ b/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp @@ -45,6 +45,14 @@ namespace CoreMidiHelpers #define CHECK_ERROR(a) CoreMidiHelpers::checkError (a, __LINE__) //============================================================================== + struct ScopedCFString + { + ScopedCFString() noexcept : cfString (nullptr) {} + ~ScopedCFString() noexcept { if (cfString != nullptr) CFRelease (cfString); } + + CFStringRef cfString; + }; + static String getMidiObjectName (MIDIObjectRef entity) { String result; @@ -116,7 +124,7 @@ namespace CoreMidiHelpers if (numConnections > 0) { - const SInt32* pid = reinterpret_cast (CFDataGetBytePtr (connections)); + const SInt32* pid = reinterpret_cast (CFDataGetBytePtr (connections)); for (int i = 0; i < numConnections; ++i, ++pid) { @@ -133,7 +141,7 @@ namespace CoreMidiHelpers || connObjectType == kMIDIObjectType_ExternalDestination) { // Connected to an external device's endpoint (10.3 and later). - s = getEndpointName (static_cast (connObject), true); + s = getEndpointName (static_cast (connObject), true); } else { @@ -208,9 +216,9 @@ namespace CoreMidiHelpers // correctly when called from the message thread! jassert (MessageManager::getInstance()->isThisTheMessageThread()); - CFStringRef name = getGlobalMidiClientName().toCFString(); - CHECK_ERROR (MIDIClientCreate (name, &globalSystemChangeCallback, nullptr, &globalMidiClient)); - CFRelease (name); + CoreMidiHelpers::ScopedCFString name; + name.cfString = getGlobalMidiClientName().toCFString(); + CHECK_ERROR (MIDIClientCreate (name.cfString, &globalSystemChangeCallback, nullptr, &globalMidiClient)); } return globalMidiClient; @@ -220,12 +228,12 @@ namespace CoreMidiHelpers class MidiPortAndEndpoint { public: - MidiPortAndEndpoint (MIDIPortRef p, MIDIEndpointRef ep) + MidiPortAndEndpoint (MIDIPortRef p, MIDIEndpointRef ep) noexcept : port (p), endPoint (ep) { } - ~MidiPortAndEndpoint() + ~MidiPortAndEndpoint() noexcept { if (port != 0) MIDIPortDispose (port); @@ -234,7 +242,7 @@ namespace CoreMidiHelpers MIDIEndpointDispose (endPoint); } - void send (const MIDIPacketList* const packets) + void send (const MIDIPacketList* const packets) noexcept { if (port != 0) MIDISend (port, endPoint, packets); @@ -302,7 +310,7 @@ namespace CoreMidiHelpers static void midiInputProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* /*srcConnRefCon*/) { - static_cast (readProcRefCon)->handlePackets (pktlist); + static_cast (readProcRefCon)->handlePackets (pktlist); } } @@ -318,19 +326,18 @@ MidiOutput* MidiOutput::openDevice (int index) { MIDIEndpointRef endPoint = MIDIGetDestination ((ItemCount) index); - CFStringRef pname; - if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) + CoreMidiHelpers::ScopedCFString pname; + + if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname.cfString))) { MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient(); MIDIPortRef port; - if (client != 0 && CHECK_ERROR (MIDIOutputPortCreate (client, pname, &port))) + if (client != 0 && CHECK_ERROR (MIDIOutputPortCreate (client, pname.cfString, &port))) { mo = new MidiOutput(); mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (port, endPoint); } - - CFRelease (pname); } } @@ -339,20 +346,20 @@ MidiOutput* MidiOutput::openDevice (int index) MidiOutput* MidiOutput::createNewDevice (const String& deviceName) { - MidiOutput* mo = nullptr; MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient(); - MIDIEndpointRef endPoint; - CFStringRef name = deviceName.toCFString(); - if (client != 0 && CHECK_ERROR (MIDISourceCreate (client, name, &endPoint))) + CoreMidiHelpers::ScopedCFString name; + name.cfString = deviceName.toCFString(); + + if (client != 0 && CHECK_ERROR (MIDISourceCreate (client, name.cfString, &endPoint))) { - mo = new MidiOutput(); + MidiOutput* mo = new MidiOutput(); mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (0, endPoint); + return mo; } - CFRelease (name); - return mo; + return nullptr; } MidiOutput::~MidiOutput() @@ -370,7 +377,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) const MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); #endif - HeapBlock allocatedPackets; + HeapBlock allocatedPackets; MIDIPacketList stackPacket; MIDIPacketList* packetToSend = &stackPacket; const size_t dataSize = (size_t) message.getRawDataSize(); @@ -436,16 +443,16 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) { if (MIDIEndpointRef endPoint = MIDIGetSource ((ItemCount) index)) { - CFStringRef name; + ScopedCFString name; - if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name))) + if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name.cfString))) { if (MIDIClientRef client = getGlobalMidiClient()) { MIDIPortRef port; - ScopedPointer mpc (new MidiPortAndCallback (*callback)); + ScopedPointer mpc (new MidiPortAndCallback (*callback)); - if (CHECK_ERROR (MIDIInputPortCreate (client, name, midiInputProc, mpc, &port))) + if (CHECK_ERROR (MIDIInputPortCreate (client, name.cfString, midiInputProc, mpc, &port))) { if (CHECK_ERROR (MIDIPortConnectSource (port, endPoint, nullptr))) { @@ -465,8 +472,6 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) } } } - - CFRelease (name); } } @@ -482,13 +487,14 @@ MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallba if (MIDIClientRef client = getGlobalMidiClient()) { - ScopedPointer mpc (new MidiPortAndCallback (*callback)); + ScopedPointer mpc (new MidiPortAndCallback (*callback)); mpc->active = false; MIDIEndpointRef endPoint; - CFStringRef name = deviceName.toCFString(); + ScopedCFString name; + name.cfString = deviceName.toCFString(); - if (CHECK_ERROR (MIDIDestinationCreate (client, name, midiInputProc, mpc, &endPoint))) + if (CHECK_ERROR (MIDIDestinationCreate (client, name.cfString, midiInputProc, mpc, &endPoint))) { mpc->portAndEndpoint = new MidiPortAndEndpoint (0, endPoint); @@ -499,8 +505,6 @@ MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallba const ScopedLock sl (callbackLock); activeCallbacks.add (mpc.release()); } - - CFRelease (name); } return mi; From bac0687f958562ba72c6680bd09391bfbfaac7f1 Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 27 May 2015 10:12:07 +0100 Subject: [PATCH 02/11] Added a sustain pedal flag to the SynthesiserVoice, and improved the voice-stealing algorithm (again!) --- .../synthesisers/juce_Synthesiser.cpp | 73 ++++++++++++++----- .../synthesisers/juce_Synthesiser.h | 11 ++- 2 files changed, 64 insertions(+), 20 deletions(-) diff --git a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 2fa2c540ea..f5e0352353 100644 --- a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -32,6 +32,7 @@ SynthesiserVoice::SynthesiserVoice() currentPlayingMidiChannel (0), noteOnTime (0), keyIsDown (false), + sustainPedalDown (false), sostenutoPedalDown (false) { } @@ -296,6 +297,7 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice, voice->currentlyPlayingSound = sound; voice->keyIsDown = true; voice->sostenutoPedalDown = false; + voice->sustainPedalDown = sustainPedalsDown[midiChannel]; voice->startNote (midiNoteNumber, velocity, sound, lastPitchWheelValues [midiChannel - 1]); @@ -331,9 +333,11 @@ void Synthesiser::noteOff (const int midiChannel, if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel)) { + jassert (! voice->keyIsDown || voice->sustainPedalDown == sustainPedalsDown [midiChannel]); + voice->keyIsDown = false; - if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown)) + if (! (voice->sustainPedalDown || voice->sostenutoPedalDown)) stopVoice (voice, velocity, allowTailOff); } } @@ -427,6 +431,14 @@ void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) if (isDown) { sustainPedalsDown.setBit (midiChannel); + + for (int i = voices.size(); --i >= 0;) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->isPlayingChannel (midiChannel) && voice->isKeyDown()) + voice->sustainPedalDown = true; + } } else { @@ -434,8 +446,13 @@ void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) { SynthesiserVoice* const voice = voices.getUnchecked (i); - if (voice->isPlayingChannel (midiChannel) && ! voice->keyIsDown) - stopVoice (voice, 1.0f, true); + if (voice->isPlayingChannel (midiChannel)) + { + voice->sustainPedalDown = false; + + if (! voice->isKeyDown()) + stopVoice (voice, 1.0f, true); + } } sustainPedalsDown.clearBit (midiChannel); @@ -505,8 +522,13 @@ struct VoiceAgeSorter SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, int /*midiChannel*/, int midiNoteNumber) const { - SynthesiserVoice* bottom = nullptr; - SynthesiserVoice* top = nullptr; + // This voice-stealing algorithm applies the following heuristics: + // - Re-use the oldest notes first + // - Protect the lowest & topmost notes, even if sustained, but not if they've been released. + + // These are the voices we want to protect (ie: only steal if unavoidable) + SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase + SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase // this is a list of voices we can steal, sorted by how long they've been running Array usableVoices; @@ -516,24 +538,33 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, { SynthesiserVoice* const voice = voices.getUnchecked (i); + jassert (voice->isVoiceActive()); // We wouldn't be here otherwise + if (voice->canPlaySound (soundToPlay)) { VoiceAgeSorter sorter; usableVoices.addSorted (sorter, voice); - const int note = voice->getCurrentlyPlayingNote(); + if (! voice->isPlayingButReleased()) // Don't protect released notes + { + const int note = voice->getCurrentlyPlayingNote(); - if (bottom == nullptr || note < bottom->getCurrentlyPlayingNote()) - bottom = voice; + if (low == nullptr || note < low->getCurrentlyPlayingNote()) + low = voice; - if (top == nullptr || note > top->getCurrentlyPlayingNote()) - top = voice; + if (top == nullptr || note > top->getCurrentlyPlayingNote()) + top = voice; + } } } + // Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s) + if (top == low) + top = nullptr; + const int numUsableVoices = usableVoices.size(); - // The oldest note that's playing with the target pitch playing is ideal.. + // The oldest note that's playing with the target pitch is ideal.. for (int i = 0; i < numUsableVoices; ++i) { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); @@ -547,7 +578,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); - if (voice != bottom && voice != top && ! voice->isKeyDown() && ! voice->isSostenutoPedalDown()) + if (voice != low && voice != top && voice->isPlayingButReleased()) return voice; } @@ -556,21 +587,25 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); - if (voice != bottom && voice != top && ! voice->isKeyDown()) + if (voice != low && voice != top && ! voice->isKeyDown()) return voice; } - // At this point, all notes have fingers on them, so look for the oldest note - // that isn't the top or bottom note.. + // Oldest voice that isn't protected for (int i = 0; i < numUsableVoices; ++i) { SynthesiserVoice* const voice = usableVoices.getUnchecked (i); - if (voice != bottom && voice != top) + if (voice != low && voice != top) return voice; } - // ..otherwise, there's only one or two voices to choose from - prefer to steal the highest one: - jassert (top != nullptr || bottom != nullptr); - return (top == nullptr ? bottom : top); + // We've only got "protected" voices now: lowest note takes priority + jassert (low != nullptr); + + // Duophonic synth: give priority to the bass note: + if (top != nullptr) + return top; + + return low; } diff --git a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index 206ce26132..8756c3e657 100644 --- a/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -214,9 +214,18 @@ public: */ bool isKeyDown() const noexcept { return keyIsDown; } + /** Returns true if the sustain pedal is currently active for this voice. */ + bool isSustainPedalDown() const noexcept { return sustainPedalDown; } + /** Returns true if the sostenuto pedal is currently active for this voice. */ bool isSostenutoPedalDown() const noexcept { return sostenutoPedalDown; } + /** Returns true if a voice is sounding in its release phase **/ + bool isPlayingButReleased() const noexcept + { + return isVoiceActive() && ! (isKeyDown() || isSostenutoPedalDown() || isSustainPedalDown()); + } + /** Returns true if this voice started playing its current note before the other voice did. */ bool wasStartedBefore (const SynthesiserVoice& other) const noexcept; @@ -244,7 +253,7 @@ private: int currentlyPlayingNote, currentPlayingMidiChannel; uint32 noteOnTime; SynthesiserSound::Ptr currentlyPlayingSound; - bool keyIsDown, sostenutoPedalDown; + bool keyIsDown, sustainPedalDown, sostenutoPedalDown; #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // Note the new parameters for this method. From 9fa964881f1f4a12e43105f286a4b58a4bdf09d2 Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 27 May 2015 10:55:16 +0100 Subject: [PATCH 03/11] Fixed an edge-case assertion involving window resizing with constraints. --- modules/juce_gui_basics/components/juce_Component.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/juce_gui_basics/components/juce_Component.cpp b/modules/juce_gui_basics/components/juce_Component.cpp index e468f6235f..0d9f9ab6a4 100644 --- a/modules/juce_gui_basics/components/juce_Component.cpp +++ b/modules/juce_gui_basics/components/juce_Component.cpp @@ -1153,12 +1153,15 @@ void Component::setBounds (const int x, const int y, int w, int h) void Component::sendMovedResizedMessagesIfPending() { - if (flags.isMoveCallbackPending || flags.isResizeCallbackPending) - { - sendMovedResizedMessages (flags.isMoveCallbackPending, flags.isResizeCallbackPending); + const bool wasMoved = flags.isMoveCallbackPending; + const bool wasResized = flags.isResizeCallbackPending; + if (wasMoved || wasResized) + { flags.isMoveCallbackPending = false; flags.isResizeCallbackPending = false; + + sendMovedResizedMessages (wasMoved, wasResized); } } From 2fcabaec100b2d82702712d19e1a9e1acd85b2fa Mon Sep 17 00:00:00 2001 From: hogliux Date: Wed, 27 May 2015 15:50:12 +0100 Subject: [PATCH 04/11] Fix bug when the first display is not the main display on linux --- .../native/juce_linux_Windowing.cpp | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index 7573f8a639..4e22c8ff92 100644 --- a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -1189,7 +1189,7 @@ private: ScopedPointer screens; const int numMonitors = ScreenCount (dpy); - RROutput mainDisplay = xrandr.getOutputPrimary (dpy, RootWindow (dpy, 0)); + RROutput mainDisplay = xrandr.getOutputPrimary (dpy, RootWindow (dpy, 0)) + 1; for (int i = 0; i < numMonitors; ++i) { @@ -3722,12 +3722,45 @@ void Desktop::Displays::findDisplays (float masterScale) { DisplayGeometry& geometry = DisplayGeometry::getOrCreateInstance (display, masterScale); + // add the main display first + int mainDisplayIdx; + for (mainDisplayIdx = 0; mainDisplayIdx < geometry.infos.size(); ++mainDisplayIdx) + { + const DisplayGeometry::ExtendedInfo& info = geometry.infos.getReference (mainDisplayIdx); + if (info.isMain) + break; + } + + // no main display found then use the first + if (mainDisplayIdx >= geometry.infos.size()) + mainDisplayIdx = 0; + + // add the main display + { + const DisplayGeometry::ExtendedInfo& info = + geometry.infos.getReference (mainDisplayIdx); + Desktop::Displays::Display d; + + d.isMain = true; + d.scale = masterScale * info.scale; + d.dpi = info.dpi; + + d.totalArea = DisplayGeometry::physicalToScaled (info.totalBounds); + d.userArea = (info.usableBounds / d.scale) + info.topLeftScaled; + + displays.add (d); + } + for (int i = 0; i < geometry.infos.size(); ++i) { + // don't add the main display a second time + if (i == mainDisplayIdx) + continue; + const DisplayGeometry::ExtendedInfo& info = geometry.infos.getReference (i); Desktop::Displays::Display d; - d.isMain = info.isMain; + d.isMain = false; d.scale = masterScale * info.scale; d.dpi = info.dpi; From 4ca4ae6be81d5626aca0c0a5c4bcbeba8522bf79 Mon Sep 17 00:00:00 2001 From: hogliux Date: Wed, 27 May 2015 15:53:37 +0100 Subject: [PATCH 05/11] Remove code that was used for testing --- modules/juce_gui_basics/native/juce_linux_Windowing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index 4e22c8ff92..9a859e5023 100644 --- a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -1189,7 +1189,7 @@ private: ScopedPointer screens; const int numMonitors = ScreenCount (dpy); - RROutput mainDisplay = xrandr.getOutputPrimary (dpy, RootWindow (dpy, 0)) + 1; + RROutput mainDisplay = xrandr.getOutputPrimary (dpy, RootWindow (dpy, 0); for (int i = 0; i < numMonitors; ++i) { From 44a5a93444e6c455975ed72012a848c81528b86e Mon Sep 17 00:00:00 2001 From: hogliux Date: Wed, 27 May 2015 16:00:56 +0100 Subject: [PATCH 06/11] Fix typo --- modules/juce_gui_basics/native/juce_linux_Windowing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp index 9a859e5023..567d094c9c 100644 --- a/modules/juce_gui_basics/native/juce_linux_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_Windowing.cpp @@ -1189,7 +1189,7 @@ private: ScopedPointer screens; const int numMonitors = ScreenCount (dpy); - RROutput mainDisplay = xrandr.getOutputPrimary (dpy, RootWindow (dpy, 0); + RROutput mainDisplay = xrandr.getOutputPrimary (dpy, RootWindow (dpy, 0)); for (int i = 0; i < numMonitors; ++i) { From 0fa2b1ca5684dd783e219d907a858c228a0c75fb Mon Sep 17 00:00:00 2001 From: hogliux Date: Wed, 27 May 2015 16:32:52 +0100 Subject: [PATCH 07/11] Fix warnings on gcc on linux --- modules/juce_core/memory/juce_Memory.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/juce_core/memory/juce_Memory.h b/modules/juce_core/memory/juce_Memory.h index 57d1644dc0..72fec6bb63 100644 --- a/modules/juce_core/memory/juce_Memory.h +++ b/modules/juce_core/memory/juce_Memory.h @@ -50,7 +50,7 @@ inline void deleteAndZero (Type& pointer) { delete poi a specific number of bytes, */ template -inline Type* addBytesToPointer (Type* pointer, IntegerType bytes) noexcept { return (Type*) (((char*) pointer) + bytes); } +inline Type* addBytesToPointer (Type* basePointer, IntegerType bytes) noexcept { return (Type*) (((char*) basePointer) + bytes); } /** A handy function which returns the difference between any two pointers, in bytes. The address of the second pointer is subtracted from the first, and the difference in bytes is returned. @@ -62,7 +62,7 @@ inline int getAddressDifference (Type1* pointer1, Type2* pointer2) noexcept { r nullptr if the pointer is null. */ template -inline Type* createCopyIfNotNull (const Type* pointer) { return pointer != nullptr ? new Type (*pointer) : nullptr; } +inline Type* createCopyIfNotNull (const Type* objectToCopy) { return objectToCopy != nullptr ? new Type (*objectToCopy) : nullptr; } //============================================================================== #if JUCE_MAC || JUCE_IOS || DOXYGEN From 39a1727223c895dd37e52836641bd6064021ebd6 Mon Sep 17 00:00:00 2001 From: jules Date: Mon, 1 Jun 2015 12:07:27 +0100 Subject: [PATCH 08/11] Added some assertions in Graphics methods to catch negatively-sized rectangles. --- modules/juce_graphics/contexts/juce_GraphicsContext.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp index a0dcd80fd0..836fd6ee3c 100644 --- a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp +++ b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp @@ -32,8 +32,8 @@ namespace jassert ((int) x >= -maxVal && (int) x <= maxVal && (int) y >= -maxVal && (int) y <= maxVal - && (int) w >= -maxVal && (int) w <= maxVal - && (int) h >= -maxVal && (int) h <= maxVal); + && (int) w >= 0 && (int) w <= maxVal + && (int) h >= 0 && (int) h <= maxVal); #endif return Rectangle (x, y, w, h); @@ -427,6 +427,8 @@ void Graphics::drawRect (const Rectangle& r, int lineThickness) const void Graphics::drawRect (Rectangle r, const float lineThickness) const { + jassert (r.getWidth() >= 0.0f && r.getHeight() >= 0.0f); + RectangleList rects; rects.addWithoutMerging (r.removeFromTop (lineThickness)); rects.addWithoutMerging (r.removeFromBottom (lineThickness)); From 58105cde57aac0cf83c04ef6d38a6bf338b661c2 Mon Sep 17 00:00:00 2001 From: jules Date: Mon, 1 Jun 2015 12:28:02 +0100 Subject: [PATCH 09/11] Fix for File::isDirectory on posix when given a File::nonexistent. Also added unit tests for this. --- modules/juce_core/files/juce_File.cpp | 5 +++++ modules/juce_core/native/juce_posix_SharedCode.h | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/juce_core/files/juce_File.cpp b/modules/juce_core/files/juce_File.cpp index 19d724b0ad..4a3b0d4ce9 100644 --- a/modules/juce_core/files/juce_File.cpp +++ b/modules/juce_core/files/juce_File.cpp @@ -916,6 +916,11 @@ public: const File temp (File::getSpecialLocation (File::tempDirectory)); expect (! File::nonexistent.exists()); + expect (! File::nonexistent.existsAsFile()); + expect (! File::nonexistent.isDirectory()); + #if ! JUCE_WINDOWS + expect (File("/").isDirectory()); + #endif expect (home.isDirectory()); expect (home.exists()); expect (! home.existsAsFile()); diff --git a/modules/juce_core/native/juce_posix_SharedCode.h b/modules/juce_core/native/juce_posix_SharedCode.h index e682eae5ed..223af328f7 100644 --- a/modules/juce_core/native/juce_posix_SharedCode.h +++ b/modules/juce_core/native/juce_posix_SharedCode.h @@ -265,8 +265,8 @@ bool File::isDirectory() const { juce_statStruct info; - return fullPath.isEmpty() - || (juce_stat (fullPath, info) && ((info.st_mode & S_IFDIR) != 0)); + return fullPath.isNotEmpty() + && (juce_stat (fullPath, info) && ((info.st_mode & S_IFDIR) != 0)); } bool File::exists() const From ac3d4ce36b21fdad8ae4ee88e8a10cb4cccbbe39 Mon Sep 17 00:00:00 2001 From: jules Date: Tue, 2 Jun 2015 12:51:08 +0100 Subject: [PATCH 10/11] Fixed a spelling mistake. --- modules/juce_core/text/juce_String.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/juce_core/text/juce_String.h b/modules/juce_core/text/juce_String.h index 8fb209adb6..70a39a874a 100644 --- a/modules/juce_core/text/juce_String.h +++ b/modules/juce_core/text/juce_String.h @@ -89,12 +89,12 @@ public: */ String (const char* text, size_t maxChars); - /** Creates a string from a whcar_t character string. + /** Creates a string from a wchar_t character string. Depending on the platform, this may be treated as either UTF-32 or UTF-16. */ String (const wchar_t* text); - /** Creates a string from a whcar_t character string. + /** Creates a string from a wchar_t character string. Depending on the platform, this may be treated as either UTF-32 or UTF-16. */ String (const wchar_t* text, size_t maxChars); From b6a1d5386d5102ab1d170eea4c4673be9a8f7876 Mon Sep 17 00:00:00 2001 From: jules Date: Thu, 4 Jun 2015 16:58:42 +0100 Subject: [PATCH 11/11] Added more re-binding of vertex buffers in GL rendering code, to work around people's own GL code unbinding this. --- .../juce_opengl/opengl/juce_OpenGLContext.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp index 90c270c36e..3c63c6217f 100644 --- a/modules/juce_opengl/opengl/juce_OpenGLContext.cpp +++ b/modules/juce_opengl/opengl/juce_OpenGLContext.cpp @@ -188,6 +188,8 @@ public: context.currentRenderScale = scale; context.renderer->renderOpenGL(); clearGLError(); + + bindVertexArray(); } if (context.renderComponents) @@ -231,6 +233,14 @@ public: } } + void bindVertexArray() noexcept + { + #if JUCE_OPENGL3 + if (vertexArrayObject != 0) + glBindVertexArray (vertexArrayObject); + #endif + } + void checkViewportBounds() { const Rectangle screenBounds (component.getTopLevelComponent()->getScreenBounds()); @@ -288,11 +298,7 @@ public: context.extensions.glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, cachedImageFrameBuffer.getTextureID()); - - #if JUCE_OPENGL3 - if (vertexArrayObject != 0) - glBindVertexArray (vertexArrayObject); - #endif + bindVertexArray(); const Rectangle cacheBounds (cachedImageFrameBuffer.getWidth(), cachedImageFrameBuffer.getHeight()); context.copyTexture (cacheBounds, cacheBounds, cacheBounds.getWidth(), cacheBounds.getHeight(), false); @@ -401,7 +407,7 @@ public: if (OpenGLShaderProgram::getLanguageVersion() > 1.2) { glGenVertexArrays (1, &vertexArrayObject); - glBindVertexArray (vertexArrayObject); + bindVertexArray(); } #endif