Browse Source

Fix for return key and multi-touch handling on iOS. Added CoreText typeface support for newer OSX and iOS versions. Experimental support for latency correction in the AudioProcessorGraph. File extension fix for directories. Experimental support for openGL threading.

tags/2021-05-28
Julian Storer 14 years ago
parent
commit
7d969716f2
11 changed files with 1432 additions and 385 deletions
  1. +813
    -219
      juce_amalgamated.cpp
  2. +64
    -24
      juce_amalgamated.h
  3. +122
    -4
      src/audio/processors/juce_AudioProcessorGraph.cpp
  4. +2
    -1
      src/core/juce_StandardHeader.h
  5. +94
    -9
      src/gui/components/special/juce_OpenGLComponent.cpp
  6. +40
    -2
      src/gui/components/special/juce_OpenGLComponent.h
  7. +4
    -9
      src/io/files/juce_File.cpp
  8. +10
    -3
      src/native/mac/juce_ios_UIViewComponentPeer.mm
  9. +242
    -1
      src/native/mac/juce_mac_Fonts.mm
  10. +35
    -108
      src/native/mac/juce_mac_NSViewComponentPeer.mm
  11. +6
    -5
      src/threads/juce_WaitableEvent.h

+ 813
- 219
juce_amalgamated.cpp
File diff suppressed because it is too large
View File


+ 64
- 24
juce_amalgamated.h View File

@@ -73,7 +73,7 @@ namespace JuceDummyNamespace {}
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 53
#define JUCE_BUILDNUMBER 76
#define JUCE_BUILDNUMBER 77

/** Current Juce version number.

@@ -5681,6 +5681,8 @@ private:
#endif // __JUCE_LEAKEDOBJECTDETECTOR_JUCEHEADER__
/*** End of inlined file: juce_LeakedObjectDetector.h ***/

#undef TYPE_BOOL // (stupidly-named CoreServices definition which interferes with other libraries).

END_JUCE_NAMESPACE

#endif // __JUCE_STANDARDHEADER_JUCEHEADER__
@@ -17777,28 +17779,26 @@ private:
JUCE_DECLARE_NON_COPYABLE (PlatformUtilities);
};

#if JUCE_MAC || JUCE_IOS
#if JUCE_MAC || JUCE_IOS || DOXYGEN

/** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII.
*/
class JUCE_API ScopedAutoReleasePool
{
public:
ScopedAutoReleasePool();
~ScopedAutoReleasePool();
/** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII. */
class JUCE_API ScopedAutoReleasePool
{
public:
ScopedAutoReleasePool();
~ScopedAutoReleasePool();

private:
void* pool;
private:
void* pool;

JUCE_DECLARE_NON_COPYABLE (ScopedAutoReleasePool);
};
JUCE_DECLARE_NON_COPYABLE (ScopedAutoReleasePool);
};

#define JUCE_AUTORELEASEPOOL const JUCE_NAMESPACE::ScopedAutoReleasePool pool;
/** A macro that can be used to easily declare a local ScopedAutoReleasePool object for RAII-based obj-C autoreleasing. */
#define JUCE_AUTORELEASEPOOL const JUCE_NAMESPACE::ScopedAutoReleasePool JUCE_JOIN_MACRO (autoReleasePool_, __LINE__);

#else

#define JUCE_AUTORELEASEPOOL

#define JUCE_AUTORELEASEPOOL
#endif

#if JUCE_LINUX
@@ -22317,12 +22317,13 @@ public:
If signal() is called when nothing is waiting, the next thread to call wait()
will return immediately and reset the signal.

If the WaitableEvent is manual reset, all threads that are currently waiting on this
object will be woken.
If the WaitableEvent is manual reset, all current and future threads that wait upon this
object will be woken, until reset() is explicitly called.

If the WaitableEvent is automatic reset, and there are one or more threads waiting
on the object, then one of them will be woken up. If no threads are currently waiting,
then the next thread to call wait() will be woken up.
If the WaitableEvent is automatic reset, and one or more threads is waiting upon the object,
then one of them will be woken up. If no threads are currently waiting, then the next thread
to call wait() will be woken up. As soon as a thread is woken, the signal is automatically
reset.

@see wait, reset
*/
@@ -63814,11 +63815,21 @@ public:
/** Returns the context that this component is sharing with.
@see shareWith
*/
OpenGLContext* getShareContext() const noexcept { return contextToShareListsWith; }
OpenGLContext* getShareContext() const noexcept { return contextToShareListsWith; }

/** Flips the openGL buffers over. */
void swapBuffers();

/** Indicates whether the component should perform its rendering on a background thread.
By default, this is set to false, and the renderOpenGL() callback happens on the main
UI thread, in response to a repaint. If set to true, then the component will create
a background thread which it uses to repeatedly call renderOpenGL().
*/
void setUsingDedicatedThread (bool useDedicatedThread) noexcept;

/** Returns true if the component is performing the rendering on a background thread. */
bool isUsingDedicatedThread() const noexcept { return useThread; }

/** This replaces the normal paint() callback - use it to draw your openGL stuff.

When this is called, makeCurrentContextActive() will already have been called
@@ -63848,6 +63859,17 @@ public:
*/
virtual void newOpenGLContextCreated() = 0;

/** This method is called when the component shuts down its OpenGL context.

You can use this callback to delete textures and any other OpenGL objects you
created in the component's context.

When this callback happens, the context will have been made current
using the makeCurrentContextActive() method, so there's no need to call it
again in your code.
*/
virtual void releaseOpenGLContext() {}

/** Returns the context that will draw into this component.

This may return 0 if the component is currently invisible or hasn't currently
@@ -63900,6 +63922,19 @@ public:
*/
virtual bool renderAndSwapBuffers();

/** Wait after swapping before next render pass.

Used when rendering is running on a thread. The default is 20 millseconds, giving
a nominal frame rate of just under 50 fps.
*/
virtual void waitAfterSwapping();

/** Stop Rendering.

Use to shut down an openGLComponent properly, whether on a thread or not.
*/
virtual bool stopRendering();

/** This returns a critical section that can be used to lock the current context.

Because the context that is used by this component can change, e.g. when the
@@ -63926,6 +63961,11 @@ public:
private:
const OpenGLType type;

class OpenGLComponentRenderThread;
friend class OpenGLComponentRenderThread;
friend class ScopedPointer <OpenGLComponentRenderThread>;
ScopedPointer <OpenGLComponentRenderThread> renderThread;

class OpenGLComponentWatcher;
friend class OpenGLComponentWatcher;
friend class ScopedPointer <OpenGLComponentWatcher>;
@@ -63935,7 +63975,7 @@ private:

CriticalSection contextLock;
OpenGLPixelFormat preferredPixelFormat;
bool needToUpdateViewport;
bool needToUpdateViewport, useThread;

OpenGLContext* createContext();
void updateContextPosition();


+ 122
- 4
src/audio/processors/juce_AudioProcessorGraph.cpp View File

@@ -169,6 +169,41 @@ private:
JUCE_DECLARE_NON_COPYABLE (AddMidiBufferOp);
};
//==============================================================================
class DelayChannelOp : public AudioGraphRenderingOp
{
public:
DelayChannelOp (const int channel_, const int numSamplesDelay_)
: channel (channel_),
bufferSize (numSamplesDelay_ + 1),
readIndex (0), writeIndex (numSamplesDelay_)
{
buffer.calloc (bufferSize);
}
void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray <MidiBuffer>&, const int numSamples)
{
float* data = sharedBufferChans.getSampleData (channel, 0);
for (int i = numSamples; --i >= 0;)
{
buffer [writeIndex] = *data;
*data++ = buffer [readIndex];
if (++readIndex >= bufferSize) readIndex = 0;
if (++writeIndex >= bufferSize) writeIndex = 0;
}
}
private:
HeapBlock<float> buffer;
const int channel, bufferSize;
int readIndex, writeIndex;
JUCE_DECLARE_NON_COPYABLE (DelayChannelOp);
};
//==============================================================================
class ProcessBufferOp : public AudioGraphRenderingOp
{
@@ -223,7 +258,8 @@ public:
const Array<void*>& orderedNodes_,
Array<void*>& renderingOps)
: graph (graph_),
orderedNodes (orderedNodes_)
orderedNodes (orderedNodes_),
totalLatency (0)
{
nodeIds.add ((uint32) zeroNodeID); // first buffer is read-only zeros
channels.add (0);
@@ -237,6 +273,8 @@ public:
markAnyUnusedBuffersAsFree (i);
}
graph.setLatencySamples (totalLatency);
}
int getNumBuffersNeeded() const { return nodeIds.size(); }
@@ -253,6 +291,42 @@ private:
static bool isNodeBusy (uint32 nodeID) noexcept { return nodeID != freeNodeID && nodeID != zeroNodeID; }
Array <uint32> nodeDelayIDs;
Array <int> nodeDelays;
int totalLatency;
int getNodeDelay (const uint32 nodeID) const { return nodeDelays [nodeDelayIDs.indexOf (nodeID)]; }
void setNodeDelay (const uint32 nodeID, const int latency)
{
const int index = nodeDelayIDs.indexOf (nodeID);
if (index >= 0)
{
nodeDelays.set (index, latency);
}
else
{
nodeDelayIDs.add (nodeID);
nodeDelays.add (latency);
}
}
int getInputLatencyForNode (const uint32 nodeID) const
{
int maxLatency = 0;
for (int i = graph.getNumConnections(); --i >= 0;)
{
const AudioProcessorGraph::Connection* const c = graph.getConnection (i);
if (c->destNodeId == nodeID)
maxLatency = jmax (maxLatency, getNodeDelay (c->sourceNodeId));
}
return maxLatency;
}
//==============================================================================
void createRenderingOpsForNode (AudioProcessorGraph::Node* const node,
Array<void*>& renderingOps,
@@ -265,6 +339,8 @@ private:
Array <int> audioChannelsToUse;
int midiBufferToUse = -1;
int maxLatency = getInputLatencyForNode (node->nodeId);
for (int inputChan = 0; inputChan < numIns; ++inputChan)
{
// get a list of all the inputs to this node
@@ -326,6 +402,11 @@ private:
bufIndex = newFreeBuffer;
}
const int nodeDelay = getNodeDelay (srcNode);
if (nodeDelay < maxLatency)
renderingOps.add (new DelayChannelOp (bufIndex, maxLatency - nodeDelay));
}
else
{
@@ -348,6 +429,11 @@ private:
// we've found one of our input chans that can be re-used..
reusableInputIndex = i;
bufIndex = sourceBufIndex;
const int nodeDelay = getNodeDelay (sourceNodes.getUnchecked (i));
if (nodeDelay < maxLatency)
renderingOps.add (new DelayChannelOp (sourceBufIndex, maxLatency - nodeDelay));
break;
}
}
@@ -371,6 +457,10 @@ private:
}
reusableInputIndex = 0;
const int nodeDelay = getNodeDelay (sourceNodes.getFirst());
if (nodeDelay < maxLatency)
renderingOps.add (new DelayChannelOp (bufIndex, maxLatency - nodeDelay));
}
for (int j = 0; j < sourceNodes.size(); ++j)
@@ -380,7 +470,30 @@ private:
const int srcIndex = getBufferContaining (sourceNodes.getUnchecked(j),
sourceOutputChans.getUnchecked(j));
if (srcIndex >= 0)
renderingOps.add (new AddChannelOp (srcIndex, bufIndex));
{
const int nodeDelay = getNodeDelay (sourceNodes.getUnchecked (j));
if (nodeDelay < maxLatency)
{
if (! isBufferNeededLater (ourRenderingIndex, inputChan,
sourceNodes.getUnchecked(j),
sourceOutputChans.getUnchecked(j)))
{
renderingOps.add (new DelayChannelOp (srcIndex, maxLatency - nodeDelay));
}
else // buffer is reused elsewhere, can't be delayed
{
const int bufferToDelay = getFreeBuffer (false);
renderingOps.add (new CopyChannelOp (srcIndex, bufferToDelay));
renderingOps.add (new DelayChannelOp (bufferToDelay, maxLatency - nodeDelay));
renderingOps.add (new AddChannelOp (bufferToDelay, bufIndex));
}
}
else
{
renderingOps.add (new AddChannelOp (srcIndex, bufIndex));
}
}
}
}
}
@@ -442,8 +555,8 @@ private:
}
else
{
// probably a feedback loop, so just use an empty one..
midiBufferToUse = getFreeBuffer (true); // need to pick a buffer even if the processor doesn't use midi
// probably a feedback loop, so just use an empty one..
midiBufferToUse = getFreeBuffer (true); // need to pick a buffer even if the processor doesn't use midi
}
}
else
@@ -501,6 +614,11 @@ private:
markBufferAsContaining (midiBufferToUse, node->nodeId,
AudioProcessorGraph::midiChannelIndex);
setNodeDelay (node->nodeId, maxLatency + node->getProcessor()->getLatencySamples());
if (numOuts == 0)
totalLatency = maxLatency;
renderingOps.add (new ProcessBufferOp (node, audioChannelsToUse,
totalChans, midiBufferToUse));
}


+ 2
- 1
src/core/juce_StandardHeader.h View File

@@ -33,7 +33,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 53
#define JUCE_BUILDNUMBER 76
#define JUCE_BUILDNUMBER 77
/** Current Juce version number.
@@ -186,6 +186,7 @@ extern JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger();
#include "juce_Logger.h"
#include "../memory/juce_LeakedObjectDetector.h"
#undef TYPE_BOOL // (stupidly-named CoreServices definition which interferes with other libraries).
END_JUCE_NAMESPACE


+ 94
- 9
src/gui/components/special/juce_OpenGLComponent.cpp View File

@@ -32,6 +32,7 @@ BEGIN_JUCE_NAMESPACE
#include "juce_OpenGLComponent.h"
#include "../windows/juce_ComponentPeer.h"
#include "../layout/juce_ComponentMovementWatcher.h"
#include "../../../threads/juce_Thread.h"
//==============================================================================
@@ -129,6 +130,40 @@ OpenGLContext* OpenGLContext::getCurrentContext()
return nullptr;
}
//==============================================================================
class OpenGLComponent::OpenGLComponentRenderThread : public Thread,
public AsyncUpdater
{
public:
//==============================================================================
OpenGLComponentRenderThread (OpenGLComponent& owner_)
: Thread ("OpenGL Render"),
owner (owner_)
{
}
void run()
{
// Context will get created and callback triggered on first render
while (owner.renderAndSwapBuffers() && ! threadShouldExit())
owner.waitAfterSwapping();
triggerAsyncUpdate();
}
void handleAsyncUpdate()
{
owner.stopRendering();
}
//==============================================================================
private:
OpenGLComponent& owner;
JUCE_DECLARE_NON_COPYABLE (OpenGLComponentRenderThread);
};
//==============================================================================
class OpenGLComponent::OpenGLComponentWatcher : public ComponentMovementWatcher
@@ -150,7 +185,7 @@ public:
void componentPeerChanged()
{
const ScopedLock sl (owner->getContextLock());
owner->deleteContext();
owner->stopRendering();
}
void componentVisibilityChanged()
@@ -158,7 +193,7 @@ public:
if (! owner->isShowing())
{
const ScopedLock sl (owner->getContextLock());
owner->deleteContext();
owner->stopRendering();
}
}
@@ -284,17 +319,38 @@ void OpenGLComponent::swapBuffers()
context->swapBuffers();
}
void OpenGLComponent::setUsingDedicatedThread (bool useDedicatedThread) noexcept
{
useThread = useDedicatedThread;
}
void OpenGLComponent::paint (Graphics&)
{
if (renderAndSwapBuffers())
if (useThread)
{
ComponentPeer* const peer = getPeer();
if (renderThread == nullptr)
renderThread = new OpenGLComponentRenderThread (*this);
if (peer != nullptr)
{
const Point<int> topLeft (getScreenPosition() - peer->getScreenPosition());
peer->addMaskedRegion (topLeft.getX(), topLeft.getY(), getWidth(), getHeight());
}
if (! renderThread->isThreadRunning())
renderThread->startThread (6);
// fall-through and update the masking region
}
else
{
if (renderThread != nullptr && renderThread->isThreadRunning())
renderThread->stopThread (5000);
if (! renderAndSwapBuffers())
return;
}
ComponentPeer* const peer = getPeer();
if (peer != nullptr)
{
const Point<int> topLeft (getScreenPosition() - peer->getScreenPosition());
peer->addMaskedRegion (topLeft.getX(), topLeft.getY(), getWidth(), getHeight());
}
}
@@ -317,6 +373,35 @@ bool OpenGLComponent::renderAndSwapBuffers()
return true;
}
void OpenGLComponent::waitAfterSwapping()
{
jassert (renderThread != nullptr && Thread::getCurrentThread() == renderThread);
Thread::sleep (20);
}
bool OpenGLComponent::stopRendering()
{
const ScopedLock sl (contextLock);
if (! makeCurrentContextActive())
return false;
releaseOpenGLContext(); // callback to allow for shutdown
if (renderThread != nullptr && Thread::getCurrentThread() == renderThread)
{
// make the context inactive - if we're on a thread, this will release the context,
// so the main thread can take it and do shutdown
makeCurrentContextInactive();
}
else if (context != nullptr)
context->deleteContext();
return true;
}
void OpenGLComponent::internalRepaint (int x, int y, int w, int h)
{
Component::internalRepaint (x, y, w, h);


+ 40
- 2
src/gui/components/special/juce_OpenGLComponent.h View File

@@ -230,13 +230,23 @@ public:
/** Returns the context that this component is sharing with.
@see shareWith
*/
OpenGLContext* getShareContext() const noexcept { return contextToShareListsWith; }
OpenGLContext* getShareContext() const noexcept { return contextToShareListsWith; }
//==============================================================================
/** Flips the openGL buffers over. */
void swapBuffers();
/** Indicates whether the component should perform its rendering on a background thread.
By default, this is set to false, and the renderOpenGL() callback happens on the main
UI thread, in response to a repaint. If set to true, then the component will create
a background thread which it uses to repeatedly call renderOpenGL().
*/
void setUsingDedicatedThread (bool useDedicatedThread) noexcept;
/** Returns true if the component is performing the rendering on a background thread. */
bool isUsingDedicatedThread() const noexcept { return useThread; }
/** This replaces the normal paint() callback - use it to draw your openGL stuff.
When this is called, makeCurrentContextActive() will already have been called
@@ -266,6 +276,16 @@ public:
*/
virtual void newOpenGLContextCreated() = 0;
/** This method is called when the component shuts down its OpenGL context.
You can use this callback to delete textures and any other OpenGL objects you
created in the component's context.
When this callback happens, the context will have been made current
using the makeCurrentContextActive() method, so there's no need to call it
again in your code.
*/
virtual void releaseOpenGLContext() {}
//==============================================================================
/** Returns the context that will draw into this component.
@@ -322,6 +342,19 @@ public:
*/
virtual bool renderAndSwapBuffers();
/** Wait after swapping before next render pass.
Used when rendering is running on a thread. The default is 20 millseconds, giving
a nominal frame rate of just under 50 fps.
*/
virtual void waitAfterSwapping();
/** Stop Rendering.
Use to shut down an openGLComponent properly, whether on a thread or not.
*/
virtual bool stopRendering();
/** This returns a critical section that can be used to lock the current context.
Because the context that is used by this component can change, e.g. when the
@@ -351,6 +384,11 @@ public:
private:
const OpenGLType type;
class OpenGLComponentRenderThread;
friend class OpenGLComponentRenderThread;
friend class ScopedPointer <OpenGLComponentRenderThread>;
ScopedPointer <OpenGLComponentRenderThread> renderThread;
class OpenGLComponentWatcher;
friend class OpenGLComponentWatcher;
friend class ScopedPointer <OpenGLComponentWatcher>;
@@ -360,7 +398,7 @@ private:
CriticalSection contextLock;
OpenGLPixelFormat preferredPixelFormat;
bool needToUpdateViewport;
bool needToUpdateViewport, useThread;
OpenGLContext* createContext();
void updateContextPosition();


+ 4
- 9
src/io/files/juce_File.cpp View File

@@ -616,17 +616,12 @@ const File File::getNonexistentSibling (const bool putNumbersInBrackets) const
//==============================================================================
const String File::getFileExtension() const
{
String ext;
const int indexOfDot = fullPath.lastIndexOfChar ('.');
if (! isDirectory())
{
const int indexOfDot = fullPath.lastIndexOfChar ('.');
if (indexOfDot > fullPath.lastIndexOfChar (separator))
ext = fullPath.substring (indexOfDot);
}
if (indexOfDot > fullPath.lastIndexOfChar (separator))
return fullPath.substring (indexOfDot);
return ext;
return String::empty;
}
bool File::hasFileExtension (const String& possibleSuffix) const


+ 10
- 3
src/native/mac/juce_ios_UIViewComponentPeer.mm View File

@@ -749,13 +749,16 @@ void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, cons
currentTouches.add (touch);
}
if (isDown)
if ([touch phase] == UITouchPhaseBegan
|| [touch phase] == UITouchPhaseStationary
|| [touch phase] == UITouchPhaseMoved)
{
currentModifiers = currentModifiers.withoutMouseButtons();
handleMouseEvent (touchIndex, pos, currentModifiers, time);
currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
}
else if (isUp)
else if ([touch phase] == UITouchPhaseEnded
|| [touch phase] == UITouchPhaseCancelled)
{
currentModifiers = currentModifiers.withoutMouseButtons();
currentTouches.remove (touchIndex);
@@ -850,7 +853,11 @@ BOOL UIViewComponentPeer::textViewReplaceCharacters (const Range<int>& range, co
if (currentSelection.isEmpty())
target->setHighlightedRegion (currentSelection.withStart (currentSelection.getStart() - 1));
target->insertTextAtCaret (text);
if (text == "\r" || text == "\n" || text == "\r\n")
handleKeyPress (KeyPress::returnKey, text[0]);
else
target->insertTextAtCaret (text);
updateHiddenTextContent (target);
}


+ 242
- 1
src/native/mac/juce_mac_Fonts.mm View File

@@ -27,6 +27,245 @@
// compiled on its own).
#if JUCE_INCLUDED_FILE
#if (JUCE_MAC && defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) \
|| (JUCE_IOS && defined (__IPHONE_3_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_2)
#define JUCE_CORETEXT_AVAILABLE 1
#endif
#if JUCE_CORETEXT_AVAILABLE
//==============================================================================
class MacTypeface : public Typeface
{
public:
//==============================================================================
MacTypeface (const Font& font)
: Typeface (font.getTypefaceName()),
fontRef (nullptr),
fontHeightToCGSizeFactor (1.0f),
renderingTransform (CGAffineTransformIdentity),
ctFontRef (nullptr),
attributedStringAtts (nullptr),
ascent (0.0f),
unitsToHeightScaleFactor (0.0f)
{
JUCE_AUTORELEASEPOOL
CFStringRef name = PlatformUtilities::juceStringToCFString (font.getTypefaceName());
ctFontRef = CTFontCreateWithName (name, 1024, nullptr);
CFRelease (name);
if (ctFontRef != nullptr)
{
bool needsItalicTransform = false;
if (font.isItalic())
{
CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits (ctFontRef, 0.0, nullptr,
kCTFontItalicTrait, kCTFontItalicTrait);
if (newFont != nullptr)
{
CFRelease (ctFontRef);
ctFontRef = newFont;
}
else
{
needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform..
}
}
if (font.isBold())
{
CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits (ctFontRef, 0.0, nullptr,
kCTFontBoldTrait, kCTFontBoldTrait);
if (newFont != nullptr)
{
CFRelease (ctFontRef);
ctFontRef = newFont;
}
}
ascent = std::abs ((float) CTFontGetAscent (ctFontRef));
const float totalSize = ascent + std::abs ((float) CTFontGetDescent (ctFontRef));
ascent /= totalSize;
pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize);
if (needsItalicTransform)
{
pathTransform = pathTransform.sheared (-0.15f, 0.0f);
renderingTransform.c = 0.15f;
}
fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr);
const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef));
const float ctTotalHeight = abs (CTFontGetAscent (ctFontRef)) + abs (CTFontGetDescent (ctFontRef));
unitsToHeightScaleFactor = 1.0f / ctTotalHeight;
fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight;
const short zero = 0;
CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero);
CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };
CFTypeRef values[] = { ctFontRef, numberRef };
attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease (numberRef);
}
}
~MacTypeface()
{
if (attributedStringAtts != nullptr)
CFRelease (attributedStringAtts);
if (fontRef != nullptr)
CGFontRelease (fontRef);
if (ctFontRef != nullptr)
CFRelease (ctFontRef);
}
float getAscent() const { return ascent; }
float getDescent() const { return 1.0f - ascent; }
float getStringWidth (const String& text)
{
float x = 0;
if (ctFontRef != nullptr && text.isNotEmpty())
{
CFStringRef cfText = PlatformUtilities::juceStringToCFString (text);
CFAttributedStringRef attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
CFRelease (cfText);
CTLineRef line = CTLineCreateWithAttributedString (attribString);
CFArrayRef runArray = CTLineGetGlyphRuns (line);
for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
{
CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
CFIndex length = CTRunGetGlyphCount (run);
HeapBlock <CGSize> advances (length);
CTRunGetAdvances (run, CFRangeMake (0, 0), advances);
for (int j = 0; j < length; ++j)
x += (float) advances[j].width;
}
CFRelease (line);
CFRelease (attribString);
x *= unitsToHeightScaleFactor;
}
return x;
}
void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)
{
xOffsets.add (0);
if (ctFontRef != nullptr && text.isNotEmpty())
{
float x = 0;
CFStringRef cfText = PlatformUtilities::juceStringToCFString (text);
CFAttributedStringRef attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
CFRelease (cfText);
CTLineRef line = CTLineCreateWithAttributedString (attribString);
CFArrayRef runArray = CTLineGetGlyphRuns (line);
for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
{
CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
CFIndex length = CTRunGetGlyphCount (run);
HeapBlock <CGSize> advances (length);
CTRunGetAdvances (run, CFRangeMake (0, 0), advances);
HeapBlock <CGGlyph> glyphs (length);
CTRunGetGlyphs (run, CFRangeMake (0, 0), glyphs);
for (int j = 0; j < length; ++j)
{
x += (float) advances[j].width;
xOffsets.add (x * unitsToHeightScaleFactor);
resultGlyphs.add (glyphs[j]);
}
}
CFRelease (line);
CFRelease (attribString);
}
}
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform)
{
Path path;
if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty())
return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0),
path, transform);
return nullptr;
}
bool getOutlineForGlyph (int glyphNumber, Path& path)
{
jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty
CGPathRef pathRef = CTFontCreatePathForGlyph (ctFontRef, (CGGlyph) glyphNumber, &renderingTransform);
CGPathApply (pathRef, &path, pathApplier);
CFRelease (pathRef);
if (! pathTransform.isIdentity())
path.applyTransform (pathTransform);
return true;
}
//==============================================================================
CGFontRef fontRef;
float fontHeightToCGSizeFactor;
CGAffineTransform renderingTransform;
private:
CTFontRef ctFontRef;
CFDictionaryRef attributedStringAtts;
float ascent, unitsToHeightScaleFactor;
AffineTransform pathTransform;
static void pathApplier (void* info, const CGPathElement* const element)
{
Path& path = *static_cast<Path*> (info);
const CGPoint* const p = element->points;
switch (element->type)
{
case kCGPathElementMoveToPoint: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;
case kCGPathElementAddLineToPoint: path.lineTo ((float) p[0].x, (float) -p[0].y); break;
case kCGPathElementAddQuadCurveToPoint: path.quadraticTo ((float) p[0].x, (float) -p[0].y,
(float) p[1].x, (float) -p[1].y); break;
case kCGPathElementAddCurveToPoint: path.cubicTo ((float) p[0].x, (float) -p[0].y,
(float) p[1].x, (float) -p[1].y,
(float) p[2].x, (float) -p[2].y); break;
case kCGPathElementCloseSubpath: path.closeSubPath(); break;
default: jassertfalse; break;
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MacTypeface);
};
#else
//==============================================================================
// The stuff that follows is a mash-up that supports pre-OSX 10.5 and pre-iOS 3.2 APIs.
// (Hopefully all of this can be ditched at some point in the future).
//==============================================================================
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
#define SUPPORT_10_4_FONTS 1
#define NEW_CGFONT_FUNCTIONS_UNAVAILABLE (CGFontCreateWithFontName == 0)
@@ -482,12 +721,14 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MacTypeface);
};
#endif
//==============================================================================
const Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
{
return new MacTypeface (font);
}
//==============================================================================
const StringArray Font::findAllTypefaceNames()
{
StringArray names;


+ 35
- 108
src/native/mac/juce_mac_NSViewComponentPeer.mm View File

@@ -198,9 +198,9 @@ public:
virtual bool redirectKeyDown (NSEvent* ev);
virtual bool redirectKeyUp (NSEvent* ev);
virtual void redirectModKeyChange (NSEvent* ev);
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
virtual bool redirectPerformKeyEquivalent (NSEvent* ev);
#endif
#endif
virtual BOOL sendDragCallback (int type, id <NSDraggingInfo> sender);
@@ -378,71 +378,19 @@ END_JUCE_NAMESPACE
waitUntilDone: NO];
}
- (void) asyncMouseUp: (NSEvent*) ev
{
if (owner != nullptr)
owner->redirectMouseUp (ev);
}
- (void) asyncMouseUp: (NSEvent*) ev { if (owner != nullptr) owner->redirectMouseUp (ev); }
- (void) mouseDragged: (NSEvent*) ev { if (owner != nullptr) owner->redirectMouseDrag (ev); }
- (void) mouseMoved: (NSEvent*) ev { if (owner != nullptr) owner->redirectMouseMove (ev); }
- (void) mouseEntered: (NSEvent*) ev { if (owner != nullptr) owner->redirectMouseEnter (ev); }
- (void) mouseExited: (NSEvent*) ev { if (owner != nullptr) owner->redirectMouseExit (ev); }
- (void) scrollWheel: (NSEvent*) ev { if (owner != nullptr) owner->redirectMouseWheel (ev); }
- (void) mouseDragged: (NSEvent*) ev
{
if (owner != nullptr)
owner->redirectMouseDrag (ev);
}
- (void) mouseMoved: (NSEvent*) ev
{
if (owner != nullptr)
owner->redirectMouseMove (ev);
}
- (void) mouseEntered: (NSEvent*) ev
{
if (owner != nullptr)
owner->redirectMouseEnter (ev);
}
- (void) mouseExited: (NSEvent*) ev
{
if (owner != nullptr)
owner->redirectMouseExit (ev);
}
- (void) rightMouseDown: (NSEvent*) ev
{
[self mouseDown: ev];
}
- (void) rightMouseDragged: (NSEvent*) ev
{
[self mouseDragged: ev];
}
- (void) rightMouseUp: (NSEvent*) ev
{
[self mouseUp: ev];
}
- (void) otherMouseDown: (NSEvent*) ev
{
[self mouseDown: ev];
}
- (void) otherMouseDragged: (NSEvent*) ev
{
[self mouseDragged: ev];
}
- (void) otherMouseUp: (NSEvent*) ev
{
[self mouseUp: ev];
}
- (void) scrollWheel: (NSEvent*) ev
{
if (owner != nullptr)
owner->redirectMouseWheel (ev);
}
- (void) rightMouseDown: (NSEvent*) ev { [self mouseDown: ev]; }
- (void) rightMouseDragged: (NSEvent*) ev { [self mouseDragged: ev]; }
- (void) rightMouseUp: (NSEvent*) ev { [self mouseUp: ev]; }
- (void) otherMouseDown: (NSEvent*) ev { [self mouseDown: ev]; }
- (void) otherMouseDragged: (NSEvent*) ev { [self mouseDragged: ev]; }
- (void) otherMouseUp: (NSEvent*) ev { [self mouseUp: ev]; }
- (BOOL) acceptsFirstMouse: (NSEvent*) ev
{
@@ -641,26 +589,10 @@ END_JUCE_NAMESPACE
}
#endif
- (BOOL) becomeFirstResponder
{
if (owner != nullptr)
owner->viewFocusGain();
return true;
}
- (BOOL) resignFirstResponder
{
if (owner != nullptr)
owner->viewFocusLoss();
return true;
}
- (BOOL) becomeFirstResponder { if (owner != nullptr) owner->viewFocusGain(); return YES; }
- (BOOL) resignFirstResponder { if (owner != nullptr) owner->viewFocusLoss(); return YES; }
- (BOOL) acceptsFirstResponder
{
return owner != nullptr && owner->canBecomeKeyWindow();
}
- (BOOL) acceptsFirstResponder { return owner != nullptr && owner->canBecomeKeyWindow(); }
//==============================================================================
- (NSArray*) getSupportedDragTypes
@@ -866,14 +798,14 @@ NSViewComponentPeer::NSViewComponentPeer (Component* const component_,
isSharedWindow (viewToAttachTo != nil),
fullScreen (false),
insideDrawRect (false),
#if USE_COREGRAPHICS_RENDERING
#if USE_COREGRAPHICS_RENDERING
usingCoreGraphics (true),
#else
#else
usingCoreGraphics (false),
#endif
#endif
recursiveToFrontCall (false)
{
NSRect r = NSMakeRect (0, 0, (float) component->getWidth(),(float) component->getHeight());
NSRect r = NSMakeRect (0, 0, (float) component->getWidth(), (float) component->getHeight());
view = [[JuceNSView alloc] initWithOwner: this withFrame: r];
[view setPostsFrameChangedNotifications: YES];
@@ -1073,12 +1005,12 @@ NSRect NSViewComponentPeer::constrainRect (NSRect r)
Rectangle<int> pos (convertToRectInt (r));
Rectangle<int> original (convertToRectInt (current));
#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_ALLOWED >= MAC_OS_X_VERSION_10_6
#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_ALLOWED >= MAC_OS_X_VERSION_10_6
if ([window inLiveResize])
#else
#else
if ([window respondsToSelector: @selector (inLiveResize)]
&& [window performSelector: @selector (inLiveResize)])
#endif
#endif
{
constrainer->checkBounds (pos, original,
Desktop::getInstance().getAllMonitorDisplayAreas().getBounds(),
@@ -1111,9 +1043,9 @@ void NSViewComponentPeer::setAlpha (float newAlpha)
}
else
{
#if defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_ALLOWED >= MAC_OS_X_VERSION_10_5
#if defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_ALLOWED >= MAC_OS_X_VERSION_10_5
[view setAlphaValue: (CGFloat) newAlpha];
#else
#else
if ([view respondsToSelector: @selector (setAlphaValue:)])
{
// PITA dynamic invocation for 10.4 builds..
@@ -1216,22 +1148,17 @@ const BorderSize<int> NSViewComponentPeer::getFrameSize() const
bool NSViewComponentPeer::setAlwaysOnTop (bool alwaysOnTop)
{
if (! isSharedWindow)
{
[window setLevel: alwaysOnTop ? NSFloatingWindowLevel
: NSNormalWindowLevel];
}
return true;
}
void NSViewComponentPeer::toFront (bool makeActiveWindow)
{
if (isSharedWindow)
{
[[view superview] addSubview: view
positioned: NSWindowAbove
relativeTo: nil];
}
if (window != nil && component->isVisible())
{
@@ -1591,7 +1518,7 @@ void NSViewComponentPeer::drawRect (NSRect r)
if (! component->isOpaque())
CGContextClearRect (cg, CGContextGetClipBoundingBox (cg));
#if USE_COREGRAPHICS_RENDERING
#if USE_COREGRAPHICS_RENDERING
if (usingCoreGraphics)
{
CoreGraphicsContext context (cg, (float) [view frame].size.height);
@@ -1601,7 +1528,7 @@ void NSViewComponentPeer::drawRect (NSRect r)
insideDrawRect = false;
}
else
#endif
#endif
{
Image temp (getComponent()->isOpaque() ? Image::RGB : Image::ARGB,
(int) (r.size.width + 0.5f),
@@ -1647,9 +1574,9 @@ const StringArray NSViewComponentPeer::getAvailableRenderingEngines()
{
StringArray s (ComponentPeer::getAvailableRenderingEngines());
#if USE_COREGRAPHICS_RENDERING
#if USE_COREGRAPHICS_RENDERING
s.add ("CoreGraphics Renderer");
#endif
#endif
return s;
}
@@ -1661,13 +1588,13 @@ int NSViewComponentPeer::getCurrentRenderingEngine() const
void NSViewComponentPeer::setCurrentRenderingEngine (int index)
{
#if USE_COREGRAPHICS_RENDERING
#if USE_COREGRAPHICS_RENDERING
if (usingCoreGraphics != (index > 0))
{
usingCoreGraphics = index > 0;
[view setNeedsDisplay: true];
}
#endif
#endif
}
bool NSViewComponentPeer::canBecomeKeyWindow()
@@ -1704,7 +1631,7 @@ void Desktop::createMouseInputSources()
//==============================================================================
void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDisable, bool allowMenusAndBars)
{
#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
if (enableOrDisable)
{
[NSApp setPresentationOptions: (allowMenusAndBars ? (NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)
@@ -1715,7 +1642,7 @@ void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDis
{
[NSApp setPresentationOptions: NSApplicationPresentationDefault];
}
#else
#else
if (enableOrDisable)
{
SetSystemUIMode (kUIModeAllSuppressed, allowMenusAndBars ? kUIOptionAutoShowMenuBar : 0);
@@ -1725,7 +1652,7 @@ void Desktop::setKioskComponent (Component* kioskModeComponent, bool enableOrDis
{
SetSystemUIMode (kUIModeNormal, 0);
}
#endif
#endif
}
//==============================================================================


+ 6
- 5
src/threads/juce_WaitableEvent.h View File

@@ -79,12 +79,13 @@ public:
If signal() is called when nothing is waiting, the next thread to call wait()
will return immediately and reset the signal.
If the WaitableEvent is manual reset, all threads that are currently waiting on this
object will be woken.
If the WaitableEvent is manual reset, all current and future threads that wait upon this
object will be woken, until reset() is explicitly called.
If the WaitableEvent is automatic reset, and there are one or more threads waiting
on the object, then one of them will be woken up. If no threads are currently waiting,
then the next thread to call wait() will be woken up.
If the WaitableEvent is automatic reset, and one or more threads is waiting upon the object,
then one of them will be woken up. If no threads are currently waiting, then the next thread
to call wait() will be woken up. As soon as a thread is woken, the signal is automatically
reset.
@see wait, reset
*/


Loading…
Cancel
Save