From 3ae9ac0295c771c7afc0def8718913366fad34d4 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Thu, 8 Oct 2009 15:43:04 +0100 Subject: [PATCH] added some changes to make the core library compile on the iPhone (none of the UI code is done yet). In the process of this, fixed some endianness problems in SystemStats::getMACAddresses, and changed some of the mac code for timing, directory searching, etc to use different APIs internally. --- build/macosx/Juce.xcodeproj/project.pbxproj | 6 +- juce_Config.h | 2 +- juce_amalgamated.cpp | 8173 ++++++++++++++++- juce_amalgamated.h | 44 +- src/core/juce_Atomic.h | 2 +- src/core/juce_DataConversions.h | 4 +- src/core/juce_PlatformDefs.h | 19 + src/core/juce_PlatformUtilities.h | 8 +- src/core/juce_SystemStats.cpp | 3 +- src/core/juce_SystemStats.h | 10 +- .../filebrowser/juce_FileTreeComponent.cpp | 2 +- src/io/network/juce_Socket.cpp | 29 +- src/juce_amalgamated_template.cpp | 6 + src/native/juce_iphone_NativeCode.mm | 111 + src/native/juce_mac_NativeCode.mm | 23 +- src/native/linux/juce_linux_Network.cpp | 2 +- src/native/mac/juce_iphone_MiscUtilities.mm | 135 + src/native/mac/juce_iphone_NativeIncludes.h | 58 + src/native/mac/juce_mac_Files.mm | 298 +- src/native/mac/juce_mac_NativeIncludes.h | 6 +- src/native/mac/juce_mac_Network.mm | 86 +- src/native/mac/juce_mac_ObjCSuffix.h | 46 + src/native/mac/juce_mac_Strings.mm | 14 + src/native/mac/juce_mac_SystemStats.mm | 26 +- src/native/mac/juce_mac_Threads.mm | 4 + src/native/windows/juce_win32_Network.cpp | 9 +- src/utilities/juce_PropertiesFile.cpp | 2 +- 27 files changed, 8488 insertions(+), 640 deletions(-) create mode 100644 src/native/juce_iphone_NativeCode.mm create mode 100644 src/native/mac/juce_iphone_MiscUtilities.mm create mode 100644 src/native/mac/juce_iphone_NativeIncludes.h create mode 100644 src/native/mac/juce_mac_ObjCSuffix.h diff --git a/build/macosx/Juce.xcodeproj/project.pbxproj b/build/macosx/Juce.xcodeproj/project.pbxproj index 4083c83317..3dd0f56e42 100644 --- a/build/macosx/Juce.xcodeproj/project.pbxproj +++ b/build/macosx/Juce.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ 8484E9D5103C9595008B7C6C /* juce_mac_WebBrowserComponent.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8484E9BD103C9595008B7C6C /* juce_mac_WebBrowserComponent.mm */; }; 8484E9D8103C95A6008B7C6C /* juce_posix_SharedCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8484E9D6103C95A6008B7C6C /* juce_posix_SharedCode.h */; }; 8484E9D9103C95A6008B7C6C /* juce_posix_NamedPipe.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8484E9D7103C95A6008B7C6C /* juce_posix_NamedPipe.cpp */; }; + 84A63C02107DF286000326FD /* juce_mac_ObjCSuffix.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A63C01107DF286000326FD /* juce_mac_ObjCSuffix.h */; }; 84F1E6E710403605006A1807 /* juce_Application.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F1E6DC10403605006A1807 /* juce_Application.cpp */; }; 84F1E6E810403605006A1807 /* juce_Application.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F1E6DD10403605006A1807 /* juce_Application.h */; }; 84F1E6E910403605006A1807 /* juce_ApplicationCommandID.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F1E6DE10403605006A1807 /* juce_ApplicationCommandID.h */; }; @@ -631,6 +632,7 @@ 8484E9BD103C9595008B7C6C /* juce_mac_WebBrowserComponent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = juce_mac_WebBrowserComponent.mm; path = ../../src/native/mac/juce_mac_WebBrowserComponent.mm; sourceTree = SOURCE_ROOT; }; 8484E9D6103C95A6008B7C6C /* juce_posix_SharedCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = juce_posix_SharedCode.h; path = ../../src/native/common/juce_posix_SharedCode.h; sourceTree = SOURCE_ROOT; }; 8484E9D7103C95A6008B7C6C /* juce_posix_NamedPipe.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = juce_posix_NamedPipe.cpp; path = ../../src/native/common/juce_posix_NamedPipe.cpp; sourceTree = SOURCE_ROOT; }; + 84A63C01107DF286000326FD /* juce_mac_ObjCSuffix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = juce_mac_ObjCSuffix.h; sourceTree = ""; }; 84F1E6DC10403605006A1807 /* juce_Application.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Application.cpp; path = ../../src/application/juce_Application.cpp; sourceTree = SOURCE_ROOT; }; 84F1E6DD10403605006A1807 /* juce_Application.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = juce_Application.h; path = ../../src/application/juce_Application.h; sourceTree = SOURCE_ROOT; }; 84F1E6DE10403605006A1807 /* juce_ApplicationCommandID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = juce_ApplicationCommandID.h; path = ../../src/application/juce_ApplicationCommandID.h; sourceTree = SOURCE_ROOT; }; @@ -1251,7 +1253,6 @@ 84A4881C08A22E2400752A2B /* native mac code */ = { isa = PBXGroup; children = ( - 8484E9A4103C958A008B7C6C /* juce_mac_NativeCode.mm */, 8484E9A6103C9595008B7C6C /* juce_mac_AppleRemote.mm */, 8484E9A7103C9595008B7C6C /* juce_mac_AudioCDBurner.mm */, 8484E9A8103C9595008B7C6C /* juce_mac_CameraDevice.mm */, @@ -1266,10 +1267,12 @@ 8484E9B1103C9595008B7C6C /* juce_mac_MessageManager.mm */, 8484E9B2103C9595008B7C6C /* juce_mac_MiscUtilities.mm */, 8484E9B3103C9595008B7C6C /* juce_mac_MouseCursor.mm */, + 8484E9A4103C958A008B7C6C /* juce_mac_NativeCode.mm */, 8484E9B4103C9595008B7C6C /* juce_mac_NativeIncludes.h */, 8484E9B5103C9595008B7C6C /* juce_mac_Network.mm */, 8484E9B6103C9595008B7C6C /* juce_mac_NSViewComponent.mm */, 8484E9B7103C9595008B7C6C /* juce_mac_NSViewComponentPeer.mm */, + 84A63C01107DF286000326FD /* juce_mac_ObjCSuffix.h */, 8484E9B8103C9595008B7C6C /* juce_mac_OpenGLComponent.mm */, 8484E9B9103C9595008B7C6C /* juce_mac_QuickTimeMovieComponent.mm */, 8484E9BA103C9595008B7C6C /* juce_mac_Strings.mm */, @@ -2560,6 +2563,7 @@ 84F1ECE41040370A006A1807 /* juce_ImageConvolutionKernel.h in Headers */, 84F1ECE61040370A006A1807 /* juce_ImageFileFormat.h in Headers */, 84F1ECE81040370A006A1807 /* juce_GIFLoader.h in Headers */, + 84A63C02107DF286000326FD /* juce_mac_ObjCSuffix.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/juce_Config.h b/juce_Config.h index 689579a6cb..bc9a47cade 100644 --- a/juce_Config.h +++ b/juce_Config.h @@ -101,7 +101,7 @@ On Windows, if you enable this, you'll need to have the QuickTime SDK installed, and its header files will need to be on your include path. */ -#if ! (defined (JUCE_QUICKTIME) || defined (LINUX) || (defined (_WIN32) && ! defined (_MSC_VER))) +#if ! (defined (JUCE_QUICKTIME) || defined (LINUX) || defined (TARGET_OS_IPHONE) || defined (TARGET_IPHONE_SIMULATOR) || (defined (_WIN32) && ! defined (_MSC_VER))) #define JUCE_QUICKTIME 1 #endif diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index e3d571081c..759c7c5260 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -111,7 +111,7 @@ On Windows, if you enable this, you'll need to have the QuickTime SDK installed, and its header files will need to be on your include path. */ -#if ! (defined (JUCE_QUICKTIME) || defined (LINUX) || (defined (_WIN32) && ! defined (_MSC_VER))) +#if ! (defined (JUCE_QUICKTIME) || defined (LINUX) || defined (TARGET_OS_IPHONE) || defined (TARGET_IPHONE_SIMULATOR) || (defined (_WIN32) && ! defined (_MSC_VER))) #define JUCE_QUICKTIME 1 #endif @@ -542,6 +542,41 @@ public: #endif // __JUCE_LINUX_NATIVEINCLUDES_JUCEHEADER__ /********* End of inlined file: juce_linux_NativeIncludes.h *********/ +#elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + +/********* Start of inlined file: juce_iphone_NativeIncludes.h *********/ +#ifndef __JUCE_MAC_NATIVEINCLUDES_JUCEHEADER__ +#define __JUCE_MAC_NATIVEINCLUDES_JUCEHEADER__ + +/* + This file wraps together all the mac-specific code, so that + we can include all the native headers just once, and compile all our + platform-specific stuff in one big lump, keeping it out of the way of + the rest of the codebase. +*/ + +#import +#import +#import +#import +#import +#import +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // __JUCE_MAC_NATIVEINCLUDES_JUCEHEADER__ +/********* End of inlined file: juce_iphone_NativeIncludes.h *********/ + #else /********* Start of inlined file: juce_mac_NativeIncludes.h *********/ @@ -566,9 +601,6 @@ public: #import #import #import -#import -#import -#import #import #include @@ -579,6 +611,9 @@ public: #include #include #include +#include +#include +#include #if MACOS_10_4_OR_EARLIER #include @@ -1394,10 +1429,9 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI() juceInitialisedNonGUI = true; DBG (SystemStats::getJUCEVersion()); - Random::getSystemRandom().setSeedRandomly(); // (calling this more than once improves its randomness) juce_initialiseStrings(); SystemStats::initialiseStats(); - Random::getSystemRandom().setSeedRandomly(); // (calling this more than once improves its randomness) + Random::getSystemRandom().setSeedRandomly(); // (mustn't call this before initialiseStats() because it relies on the time being set up) } } @@ -7209,7 +7243,7 @@ END_JUCE_NAMESPACE #include #include #else - #if MACOSX_DEPLOYMENT_TARGET <= MAC_OS_X_VERSION_10_4 + #if (MACOSX_DEPLOYMENT_TARGET <= MAC_OS_X_VERSION_10_4) && ! (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR) #include #endif #endif @@ -7222,6 +7256,12 @@ END_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE +#if defined (JUCE_LINUX) || defined (JUCE_MAC) || defined (JUCE_IPHONE) + typedef socklen_t juce_socklen_t; +#else + typedef int juce_socklen_t; +#endif + #if JUCE_WIN32 typedef int (__stdcall juce_CloseWin32SocketLibCall) (void); @@ -7353,12 +7393,7 @@ static int waitForReadiness (const int handle, const bool forReading, { int opt; - -#if defined (JUCE_LINUX) || defined (JUCE_MAC) - socklen_t len = sizeof (opt); -#else - int len = sizeof (opt); -#endif + juce_socklen_t len = sizeof (opt); if (getsockopt (handle, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0 || opt != 0) @@ -7627,12 +7662,7 @@ StreamingSocket* StreamingSocket::waitForNextConnection() const if (connected && isListener) { struct sockaddr address; - -#if defined (JUCE_LINUX) || defined (JUCE_MAC) - socklen_t len = sizeof (sockaddr); -#else - int len = sizeof (sockaddr); -#endif + juce_socklen_t len = sizeof (sockaddr); const int newSocket = (int) accept (handle, &address, &len); if (newSocket >= 0 && connected) @@ -7734,12 +7764,7 @@ bool DatagramSocket::connect (const String& remoteHostName, DatagramSocket* DatagramSocket::waitForNextConnection() const { struct sockaddr address; - -#if defined (JUCE_LINUX) || defined (JUCE_MAC) - socklen_t len = sizeof (sockaddr); -#else - int len = sizeof (sockaddr); -#endif + juce_socklen_t len = sizeof (sockaddr); while (waitUntilReady (true, -1) == 1) { @@ -17265,7 +17290,7 @@ const File PropertiesFile::getDefaultAppSettingsFile (const String& applicationN // mustn't have illegal characters in this name.. jassert (applicationName == File::createLegalFileName (applicationName)); -#if JUCE_MAC +#if JUCE_MAC || JUCE_IPHONE File dir (commonToAllUsers ? "/Library/Preferences" : "~/Library/Preferences"); @@ -55043,7 +55068,7 @@ public: void paintItem (Graphics& g, int width, int height) { - if (file != File::nonexistent && ! isDirectory) + if (file != File::nonexistent) { updateIcon (true); @@ -233612,12 +233637,9 @@ static int getMACAddressesViaNetBios (int64* addresses, int maxNum, const bool l { if (astat.adapt.adapter_type == 0xfe) { - int64 mac = 0; - for (unsigned int i = 0; i < 6; ++i) - mac = (mac << 8) | astat.adapt.adapter_address[i]; - - if (littleEndian) - mac = (int64) swapByteOrder ((uint64) mac); + uint64 mac = 0; + for (int i = 6; --i >= 0;) + mac = (mac << 8) | astat.adapt.adapter_address [littleEndian ? i : (5 - i)]; if (numFound < maxNum && mac != 0) addresses [numFound++] = mac; @@ -249321,7 +249343,7 @@ int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littl { int64 a = 0; for (int j = 6; --j >= 0;) - a = (a << 8) | (uint8) ifr.ifr_hwaddr.sa_data[j]; + a = (a << 8) | (uint8) ifr.ifr_hwaddr.sa_data [littleEndian ? j : (5 - j)]; *addresses++ = a; ++numResults; @@ -256010,6 +256032,11 @@ BEGIN_JUCE_NAMESPACE #undef Point +#define JUCE_INCLUDED_FILE 1 + +// Now include the actual code files.. + +/********* Start of inlined file: juce_mac_ObjCSuffix.h *********/ /** This suffix is used for naming all Obj-C classes that are used inside juce. Because of the flat naming structure used by Obj-C, you can get horrible situations where @@ -256030,10 +256057,7 @@ BEGIN_JUCE_NAMESPACE #define appendMacro1(a, b, c, d) a ## _ ## b ## _ ## c ## _ ## d #define appendMacro2(a, b, c, d) appendMacro1(a, b, c, d) #define MakeObjCClassName(rootName) appendMacro2 (rootName, JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_ObjCExtraSuffix) - -#define JUCE_INCLUDED_FILE 1 - -// Now include the actual code files.. +/********* End of inlined file: juce_mac_ObjCSuffix.h *********/ /********* Start of inlined file: juce_mac_Strings.mm *********/ // (This file gets included by juce_mac_NativeCode.mm, rather than being @@ -256113,6 +256137,10 @@ CFStringRef PlatformUtilities::juceStringToCFString (const String& s) const String PlatformUtilities::convertToPrecomposedUnicode (const String& s) { +#if JUCE_IPHONE + const ScopedAutoReleasePool pool; + return nsStringToJuce ([juceStringToNS (s) precomposedStringWithCanonicalMapping]); +#else UnicodeMapping map; map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault, @@ -256166,22 +256194,32 @@ const String PlatformUtilities::convertToPrecomposedUnicode (const String& s) } return result; +#endif } #if ! JUCE_ONLY_BUILD_CORE_LIBRARY void SystemClipboard::copyTextToClipboard (const String& text) throw() { +#if JUCE_IPHONE + [[UIPasteboard generalPasteboard] setValue: juceStringToNS (text) + forPasteboardType: (NSString*) kUTTypePlainText]; +#else [[NSPasteboard generalPasteboard] declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: nil]; [[NSPasteboard generalPasteboard] setString: juceStringToNS (text) forType: NSStringPboardType]; +#endif } const String SystemClipboard::getTextFromClipboard() throw() { +#if JUCE_IPHONE + NSString* text = [[UIPasteboard generalPasteboard] valueForPasteboardType: (NSString*) kUTTypePlainText]; +#else NSString* text = [[NSPasteboard generalPasteboard] stringForType: NSStringPboardType]; +#endif return text == 0 ? String::empty : nsStringToJuce (text); @@ -256197,7 +256235,8 @@ const String SystemClipboard::getTextFromClipboard() throw() // compiled on its own). #ifdef JUCE_INCLUDED_FILE -static int64 highResTimerFrequency; +static int64 highResTimerFrequency = 0; +static double highResTimerToMillisecRatio = 0; #if JUCE_INTEL @@ -256252,11 +256291,13 @@ void SystemStats::initialiseStats() throw() { initialised = true; +#if JUCE_MAC // extremely annoying: adding this line stops the apple menu items from working. Of // course, not adding it means that carbon windows (e.g. in plugins) won't get // any events. //NSApplicationLoad(); [NSApplication sharedApplication]; +#endif #if JUCE_INTEL { @@ -256270,7 +256311,10 @@ void SystemStats::initialiseStats() throw() } #endif - highResTimerFrequency = (int64) AudioGetHostClockFrequency(); + mach_timebase_info_data_t timebase; + (void) mach_timebase_info (&timebase); + highResTimerFrequency = (int64) (1.0e9 * timebase.denom / timebase.numer); + highResTimerToMillisecRatio = timebase.numer / (1.0e6 * timebase.denom); String s (SystemStats::getJUCEVersion()); @@ -256380,27 +256424,19 @@ int SystemStats::getNumCpus() throw() #endif } -static int64 juce_getMicroseconds() throw() -{ - UnsignedWide t; - Microseconds (&t); - return (((int64) t.hi) << 32) | t.lo; -} - uint32 juce_millisecondsSinceStartup() throw() { - return (uint32) (juce_getMicroseconds() / 1000); + return (uint32) (mach_absolute_time() * highResTimerToMillisecRatio); } double Time::getMillisecondCounterHiRes() throw() { - // xxx might be more accurate to use a scaled AudioGetCurrentHostTime? - return juce_getMicroseconds() * 0.001; + return mach_absolute_time() * highResTimerToMillisecRatio; } int64 Time::getHighResolutionTicks() throw() { - return (int64) AudioGetCurrentHostTime(); + return (int64) mach_absolute_time(); } int64 Time::getHighResolutionTicksPerSecond() throw() @@ -256410,7 +256446,7 @@ int64 Time::getHighResolutionTicksPerSecond() throw() int64 SystemStats::getClockCycleCounter() throw() { - return (int64) AudioGetCurrentHostTime(); + return (int64) mach_absolute_time(); } bool Time::setSystemTimeToThisTime() const throw() @@ -256436,84 +256472,42 @@ void PlatformUtilities::fpuReset() // compiled on its own). #if JUCE_INCLUDED_FILE -static bool getEthernetIterator (io_iterator_t* matchingServices) throw() -{ - mach_port_t masterPort; - - if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS) - { - CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass); - - if (dict != 0) - { - CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - if (propDict != 0) - { - CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue); - - CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict); - CFRelease (propDict); - } - } - - return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS; - } - - return false; -} - int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() { + #ifndef IFT_ETHER + #define IFT_ETHER 6 + #endif + + ifaddrs* addrs = 0; int numResults = 0; - io_iterator_t it; - if (getEthernetIterator (&it)) + if (getifaddrs (&addrs) == 0) { - io_object_t i; + const ifaddrs* cursor = addrs; - while ((i = IOIteratorNext (it)) != 0) + while (cursor != 0 && numResults < maxNum) { - io_object_t controller; - - if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS) + if (cursor->ifa_addr->sa_family == AF_LINK) { - CFTypeRef data = IORegistryEntryCreateCFProperty (controller, - CFSTR (kIOMACAddress), - kCFAllocatorDefault, - 0); - if (data != 0) + const sockaddr_dl* const sadd = (const sockaddr_dl*) cursor->ifa_addr; + + if (sadd->sdl_type == IFT_ETHER) { - UInt8 addr [kIOEthernetAddressSize]; - zeromem (addr, sizeof (addr)); + const uint8* const addr = ((const uint8*) sadd->sdl_data) + sadd->sdl_nlen; - CFDataGetBytes ((CFDataRef) data, CFRangeMake (0, sizeof (addr)), addr); - CFRelease (data); - - int64 a = 0; + uint64 a = 0; for (int i = 6; --i >= 0;) - a = (a << 8) | addr[i]; + a = (a << 8) | addr [littleEndian ? i : (5 - i)]; - if (! littleEndian) - a = (int64) swapByteOrder ((uint64) a); - - if (numResults < maxNum) - { - *addresses++ = a; - ++numResults; - } + *addresses++ = (int64) a; + ++numResults; } - - IOObjectRelease (controller); } - IOObjectRelease (i); + cursor = cursor->ifa_next; } - IOObjectRelease (it); + freeifaddrs (addrs); } return numResults; @@ -256524,6 +256518,11 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd const String& bodyText, const StringArray& filesToAttach) { +#if JUCE_IPHONE + //xxx probably need to use MFMailComposeViewController + jassertfalse + return false; +#else const ScopedAutoReleasePool pool; String script; @@ -256559,6 +256558,7 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd [s release]; return ok; +#endif } END_JUCE_NAMESPACE @@ -257124,7 +257124,11 @@ void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() bool Process::isForegroundProcess() throw() { +#if JUCE_MAC return [NSApp isActive]; +#else + return true; // xxx change this if more than one app is ever possible on the iPhone! +#endif } void Process::raisePrivilege() @@ -257634,28 +257638,6 @@ void InterProcessLock::exit() throw() live in juce_posix_SharedCode.h! */ -const unsigned int macTimeToUnixTimeDiff = 0x7c25be90; - -static uint64 utcDateTimeToUnixTime (const UTCDateTime& d) throw() -{ - if (d.highSeconds == 0 && d.lowSeconds == 0 && d.fraction == 0) - return 0; - - return (((((uint64) d.highSeconds) << 32) | (uint64) d.lowSeconds) * 1000) - + ((d.fraction * 1000) >> 16) - - 2082844800000ll; -} - -static void unixTimeToUtcDateTime (uint64 t, UTCDateTime& d) throw() -{ - if (t != 0) - t += 2082844800000ll; - - d.highSeconds = (t / 1000) >> 32; - d.lowSeconds = (t / 1000) & (uint64) 0xffffffff; - d.fraction = ((t % 1000) << 16) / 1000; -} - void juce_getFileTimes (const String& fileName, int64& modificationTime, int64& accessTime, @@ -257665,24 +257647,13 @@ void juce_getFileTimes (const String& fileName, accessTime = 0; creationTime = 0; - FSRef fileRef; - if (PlatformUtilities::makeFSRefFromPath (&fileRef, fileName)) + struct stat info; + const int res = stat (fileName.toUTF8(), &info); + if (res == 0) { - FSRefParam info; - zerostruct (info); - - info.ref = &fileRef; - info.whichInfo = kFSCatInfoAllDates; - - FSCatalogInfo catInfo; - info.catInfo = &catInfo; - - if (PBGetCatalogInfoSync (&info) == noErr) - { - creationTime = utcDateTimeToUnixTime (catInfo.createDate); - accessTime = utcDateTimeToUnixTime (catInfo.accessDate); - modificationTime = utcDateTimeToUnixTime (catInfo.contentModDate); - } + modificationTime = (int64) info.st_mtime * 1000; + accessTime = (int64) info.st_atime * 1000; + creationTime = (int64) info.st_ctime * 1000; } } @@ -257691,59 +257662,29 @@ bool juce_setFileTimes (const String& fileName, int64 accessTime, int64 creationTime) throw() { - FSRef fileRef; - if (PlatformUtilities::makeFSRefFromPath (&fileRef, fileName)) - { - FSRefParam info; - zerostruct (info); + struct utimbuf times; + times.actime = (time_t) (accessTime / 1000); + times.modtime = (time_t) (modificationTime / 1000); - info.ref = &fileRef; - info.whichInfo = kFSCatInfoAllDates; - - FSCatalogInfo catInfo; - info.catInfo = &catInfo; - - if (PBGetCatalogInfoSync (&info) == noErr) - { - if (creationTime != 0) - unixTimeToUtcDateTime (creationTime, catInfo.createDate); - - if (modificationTime != 0) - unixTimeToUtcDateTime (modificationTime, catInfo.contentModDate); - - if (accessTime != 0) - unixTimeToUtcDateTime (accessTime, catInfo.accessDate); - - return PBSetCatalogInfoSync (&info) == noErr; - } - } - - return false; + return utime (fileName.toUTF8(), ×) == 0; } bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) throw() { - const char* const fileNameUTF8 = fileName.toUTF8(); - struct stat info; - const int res = stat (fileNameUTF8, &info); + const int res = stat (fileName.toUTF8(), &info); + if (res != 0) + return false; - bool ok = false; + info.st_mode &= 0777; // Just permissions - if (res == 0) - { - info.st_mode &= 0777; // Just permissions + if (isReadOnly) + info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + else + // Give everybody write permission? + info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; - if (isReadOnly) - info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); - else - // Give everybody write permission? - info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; - - ok = chmod (fileNameUTF8, info.st_mode) == 0; - } - - return ok; + return chmod (fileName.toUTF8(), info.st_mode) == 0; } bool juce_copyFile (const String& src, const String& dst) throw() @@ -257802,22 +257743,29 @@ bool File::isOnHardDisk() const throw() bool File::isOnRemovableDrive() const throw() { - const ScopedAutoReleasePool pool; - BOOL removable = false; +#if JUCE_IPHONE + return false; // xxx is this possible? +#else + const ScopedAutoReleasePool pool; + BOOL removable = false; - [[NSWorkspace sharedWorkspace] - getFileSystemInfoForPath: juceStringToNS (getFullPathName()) - isRemovable: &removable - isWritable: nil - isUnmountable: nil - description: nil - type: nil]; + [[NSWorkspace sharedWorkspace] + getFileSystemInfoForPath: juceStringToNS (getFullPathName()) + isRemovable: &removable + isWritable: nil + isUnmountable: nil + description: nil + type: nil]; return removable; +#endif } static bool juce_isHiddenFile (const String& path) throw() { +#if JUCE_IPHONE + return File (path).getFileName().startsWithChar (T('.')); +#else FSRef ref; if (! PlatformUtilities::makeFSRefFromPath (&ref, path)) return false; @@ -257829,6 +257777,7 @@ static bool juce_isHiddenFile (const String& path) throw() return (((FolderInfo*) &info.finderInfo)->finderFlags & kIsInvisible) != 0; return (((FileInfo*) &info.finderInfo)->finderFlags & kIsInvisible) != 0; +#endif } bool File::isHidden() const throw() @@ -257946,15 +257895,10 @@ const String File::getVersion() const throw() const File File::getLinkedTarget() const throw() { - FSRef ref; - Boolean targetIsAFolder, wasAliased; + NSString* dest = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath: juceStringToNS (getFullPathName()) error: nil]; - if (PlatformUtilities::makeFSRefFromPath (&ref, getFullPathName()) - && (FSResolveAliasFileWithMountFlags (&ref, true, &targetIsAFolder, &wasAliased, 0) == noErr) - && wasAliased) - { - return File (PlatformUtilities::makePathFromFSRef (&ref)); - } + if (dest != nil) + return File (nsStringToJuce (dest)); return *this; } @@ -257964,6 +257908,9 @@ bool File::moveToTrash() const throw() if (! exists()) return true; +#if JUCE_IPHONE + return deleteFile(); //xxx is there a trashcan on the iPhone? +#else const ScopedAutoReleasePool pool; NSString* p = juceStringToNS (getFullPathName()); @@ -257974,99 +257921,35 @@ bool File::moveToTrash() const throw() destination: @"" files: [NSArray arrayWithObject: [p lastPathComponent]] tag: nil ]; +#endif } struct FindFileStruct { - String parentDir, wildCard; - DIR* dir; - - bool getNextMatch (String& result, bool* const isDir, bool* const isHidden, int64* const fileSize, - Time* const modTime, Time* const creationTime, bool* const isReadOnly) throw() - { - const char* const wildCardUTF8 = wildCard.toUTF8(); - - for (;;) - { - struct dirent* const de = readdir (dir); - - if (de == 0) - break; - - if (fnmatch (wildCardUTF8, de->d_name, 0) == 0) - { - result = String::fromUTF8 ((const uint8*) de->d_name); - - const String path (parentDir + result); - - if (isDir != 0 || fileSize != 0) - { - struct stat info; - const bool statOk = juce_stat (path, info); - - if (isDir != 0) - *isDir = path.isEmpty() || (statOk && ((info.st_mode & S_IFDIR) != 0)); - - if (isHidden != 0) - *isHidden = (de->d_name[0] == '.') - || juce_isHiddenFile (path); - - if (fileSize != 0) - *fileSize = statOk ? info.st_size : 0; - } - - if (modTime != 0 || creationTime != 0) - { - int64 m, a, c; - juce_getFileTimes (path, m, a, c); - - if (modTime != 0) - *modTime = m; - - if (creationTime != 0) - *creationTime = c; - } - - if (isReadOnly != 0) - *isReadOnly = ! juce_canWriteToFile (path); - - return true; - } - } - - return false; - } + NSDirectoryEnumerator* enumerator; + String parentDir; }; -// returns 0 on failure void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) throw() { - DIR* const d = opendir (directory.toUTF8()); + NSDirectoryEnumerator* e = [[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory)]; - if (d != 0) + if (e != 0) { - FindFileStruct* const ff = new FindFileStruct(); + FindFileStruct* ff = new FindFileStruct(); + ff->enumerator = [e retain]; ff->parentDir = directory; - if (!ff->parentDir.endsWithChar (File::separator)) + if (! ff->parentDir.endsWithChar (File::separator)) ff->parentDir += File::separator; - ff->wildCard = wildCard; - ff->dir = d; - - if (ff->getNextMatch (firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly)) - { + if (juce_findFileNext (ff, firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly)) return ff; - } - else - { - firstResultFile = String::empty; - isDir = false; - closedir (d); - delete ff; - } + + [e release]; + delete ff; } return 0; @@ -258075,23 +257958,55 @@ void* juce_findFileStart (const String& directory, const String& wildCard, Strin bool juce_findFileNext (void* handle, String& resultFile, bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) throw() { - FindFileStruct* const ff = (FindFileStruct*) handle; + FindFileStruct* ff = (FindFileStruct*) handle; + NSString* file; - if (ff != 0) - return ff->getNextMatch (resultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); + if (ff == 0 || (file = [ff->enumerator nextObject]) == 0) + return false; - return false; + [ff->enumerator skipDescendents]; + resultFile = nsStringToJuce (file); + + const String path (ff->parentDir + resultFile); + + if (isDir != 0 || fileSize != 0) + { + struct stat info; + const bool statOk = juce_stat (path, info); + + if (isDir != 0) + *isDir = statOk && ((info.st_mode & S_IFDIR) != 0); + + if (isHidden != 0) + *isHidden = juce_isHiddenFile (path); + + if (fileSize != 0) + *fileSize = statOk ? info.st_size : 0; + } + + if (modTime != 0 || creationTime != 0) + { + int64 m, a, c; + juce_getFileTimes (path, m, a, c); + + if (modTime != 0) + *modTime = m; + + if (creationTime != 0) + *creationTime = c; + } + + if (isReadOnly != 0) + *isReadOnly = ! juce_canWriteToFile (path); + + return true; } void juce_findFileClose (void* handle) throw() { - FindFileStruct* const ff = (FindFileStruct*)handle; - - if (ff != 0) - { - closedir (ff->dir); - delete ff; - } + FindFileStruct* ff = (FindFileStruct*) handle; + [ff->enumerator release]; + delete ff; } bool juce_launchExecutable (const String& pathAndArguments) throw() @@ -258118,6 +258033,9 @@ bool juce_launchExecutable (const String& pathAndArguments) throw() bool juce_launchFile (const String& fileName, const String& parameters) throw() { +#if JUCE_IPHONE + return false; // is this possible? +#else const ScopedAutoReleasePool pool; if (parameters.isEmpty()) @@ -258153,8 +258071,10 @@ bool juce_launchFile (const String& fileName, } return ok; +#endif } +#if ! JUCE_IPHONE bool PlatformUtilities::makeFSRefFromPath (FSRef* destFSRef, const String& path) { return FSPathMakeRef ((const UInt8*) path.toUTF8(), destFSRef, 0) == noErr; @@ -258172,17 +258092,23 @@ const String PlatformUtilities::makePathFromFSRef (FSRef* file) return PlatformUtilities::convertToPrecomposedUnicode (result); } +#endif OSType PlatformUtilities::getTypeOfFile (const String& filename) { const ScopedAutoReleasePool pool; - return NSHFSTypeCodeFromFileType (NSHFSTypeOfFile (juceStringToNS (filename))); + NSDictionary* fileDict = [[NSFileManager defaultManager] fileAttributesAtPath: juceStringToNS (filename) traverseLink: NO]; + return (OSType) [fileDict objectForKey: NSFileHFSTypeCode]; } bool PlatformUtilities::isBundle (const String& filename) { +#if JUCE_IPHONE + return false; // xxx can't find a sensible way to do this without trying to open the bundle.. +#else const ScopedAutoReleasePool pool; return [[NSWorkspace sharedWorkspace] isFilePackageAtPath: juceStringToNS (filename)]; +#endif } #endif @@ -265403,3 +265329,7648 @@ END_JUCE_NAMESPACE /********* End of inlined file: juce_mac_NativeCode.mm *********/ #endif + +#if JUCE_IPHONE + +/********* Start of inlined file: juce_iphone_NativeCode.mm *********/ +/* + This file wraps together all the mac-specific code, so that + we can include all the native headers just once, and compile all our + platform-specific stuff in one big lump, keeping it out of the way of + the rest of the codebase. +*/ + +BEGIN_JUCE_NAMESPACE + +#undef Point + +#define JUCE_INCLUDED_FILE 1 + +// Now include the actual code files.. + +/********* Start of inlined file: juce_mac_ObjCSuffix.h *********/ +/** This suffix is used for naming all Obj-C classes that are used inside juce. + + Because of the flat naming structure used by Obj-C, you can get horrible situations where + two DLLs are loaded into a host, each of which uses classes with the same names, and these get + cross-linked so that when you make a call to a class that you thought was private, it ends up + actually calling into a similarly named class in the other module's address space. + + By changing this macro to a unique value, you ensure that all the obj-C classes in your app + have unique names, and should avoid this problem. + + If you're using the amalgamated version, you can just set this macro to something unique before + you include juce_amalgamated.cpp. +*/ +#ifndef JUCE_ObjCExtraSuffix + #define JUCE_ObjCExtraSuffix 3 +#endif + +#define appendMacro1(a, b, c, d) a ## _ ## b ## _ ## c ## _ ## d +#define appendMacro2(a, b, c, d) appendMacro1(a, b, c, d) +#define MakeObjCClassName(rootName) appendMacro2 (rootName, JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_ObjCExtraSuffix) +/********* End of inlined file: juce_mac_ObjCSuffix.h *********/ + +/********* Start of inlined file: juce_mac_Strings.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +static const String nsStringToJuce (NSString* s) +{ + return String::fromUTF8 ((uint8*) [s UTF8String]); +} + +static NSString* juceStringToNS (const String& s) +{ + return [NSString stringWithUTF8String: (const char*) s.toUTF8()]; +} + +static const String convertUTF16ToString (const UniChar* utf16) +{ + String s; + + while (*utf16 != 0) + s += (juce_wchar) *utf16++; + + return s; +} + +const String PlatformUtilities::cfStringToJuceString (CFStringRef cfString) +{ + String result; + + if (cfString != 0) + { +#if JUCE_STRINGS_ARE_UNICODE + CFRange range = { 0, CFStringGetLength (cfString) }; + UniChar* const u = (UniChar*) juce_malloc (sizeof (UniChar) * (range.length + 1)); + + CFStringGetCharacters (cfString, range, u); + u[range.length] = 0; + + result = convertUTF16ToString (u); + + juce_free (u); +#else + const int len = CFStringGetLength (cfString); + char* buffer = (char*) juce_malloc (len + 1); + CFStringGetCString (cfString, buffer, len + 1, CFStringGetSystemEncoding()); + result = buffer; + juce_free (buffer); +#endif + } + + return result; +} + +CFStringRef PlatformUtilities::juceStringToCFString (const String& s) +{ +#if JUCE_STRINGS_ARE_UNICODE + const int len = s.length(); + const juce_wchar* t = (const juce_wchar*) s; + + UniChar* temp = (UniChar*) juce_malloc (sizeof (UniChar) * len + 4); + + for (int i = 0; i <= len; ++i) + temp[i] = t[i]; + + CFStringRef result = CFStringCreateWithCharacters (kCFAllocatorDefault, temp, len); + juce_free (temp); + + return result; + +#else + return CFStringCreateWithCString (kCFAllocatorDefault, + (const char*) s, + CFStringGetSystemEncoding()); +#endif +} + +const String PlatformUtilities::convertToPrecomposedUnicode (const String& s) +{ +#if JUCE_IPHONE + const ScopedAutoReleasePool pool; + return nsStringToJuce ([juceStringToNS (s) precomposedStringWithCanonicalMapping]); +#else + UnicodeMapping map; + + map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault, + kUnicodeNoSubset, + kTextEncodingDefaultFormat); + + map.otherEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault, + kUnicodeCanonicalCompVariant, + kTextEncodingDefaultFormat); + + map.mappingVersion = kUnicodeUseLatestMapping; + + UnicodeToTextInfo conversionInfo = 0; + String result; + + if (CreateUnicodeToTextInfo (&map, &conversionInfo) == noErr) + { + const int len = s.length(); + + UniChar* const tempIn = (UniChar*) juce_calloc (sizeof (UniChar) * len + 4); + UniChar* const tempOut = (UniChar*) juce_calloc (sizeof (UniChar) * len + 4); + + for (int i = 0; i <= len; ++i) + tempIn[i] = s[i]; + + ByteCount bytesRead = 0; + ByteCount outputBufferSize = 0; + + if (ConvertFromUnicodeToText (conversionInfo, + len * sizeof (UniChar), tempIn, + kUnicodeDefaultDirectionMask, + 0, 0, 0, 0, + len * sizeof (UniChar), &bytesRead, + &outputBufferSize, tempOut) == noErr) + { + result.preallocateStorage (bytesRead / sizeof (UniChar) + 2); + + tchar* t = const_cast ((const tchar*) result); + + unsigned int i; + for (i = 0; i < bytesRead / sizeof (UniChar); ++i) + t[i] = (tchar) tempOut[i]; + + t[i] = 0; + } + + juce_free (tempIn); + juce_free (tempOut); + + DisposeUnicodeToTextInfo (&conversionInfo); + } + + return result; +#endif +} + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + +void SystemClipboard::copyTextToClipboard (const String& text) throw() +{ +#if JUCE_IPHONE + [[UIPasteboard generalPasteboard] setValue: juceStringToNS (text) + forPasteboardType: (NSString*) kUTTypePlainText]; +#else + [[NSPasteboard generalPasteboard] declareTypes: [NSArray arrayWithObject: NSStringPboardType] + owner: nil]; + + [[NSPasteboard generalPasteboard] setString: juceStringToNS (text) + forType: NSStringPboardType]; +#endif +} + +const String SystemClipboard::getTextFromClipboard() throw() +{ +#if JUCE_IPHONE + NSString* text = [[UIPasteboard generalPasteboard] valueForPasteboardType: (NSString*) kUTTypePlainText]; +#else + NSString* text = [[NSPasteboard generalPasteboard] stringForType: NSStringPboardType]; +#endif + + return text == 0 ? String::empty + : nsStringToJuce (text); +} + +#endif + +#endif +/********* End of inlined file: juce_mac_Strings.mm *********/ + +/********* Start of inlined file: juce_mac_SystemStats.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +static int64 highResTimerFrequency = 0; +static double highResTimerToMillisecRatio = 0; + +#if JUCE_INTEL + +static void juce_getCpuVendor (char* const v) throw() +{ + int vendor[4]; + zerostruct (vendor); + int dummy = 0; + + asm ("mov %%ebx, %%esi \n\t" + "cpuid \n\t" + "xchg %%esi, %%ebx" + : "=a" (dummy), "=S" (vendor[0]), "=c" (vendor[2]), "=d" (vendor[1]) : "a" (0)); + + memcpy (v, vendor, 16); +} + +static unsigned int getCPUIDWord (unsigned int& familyModel, unsigned int& extFeatures) throw() +{ + unsigned int cpu = 0; + unsigned int ext = 0; + unsigned int family = 0; + unsigned int dummy = 0; + + asm ("mov %%ebx, %%esi \n\t" + "cpuid \n\t" + "xchg %%esi, %%ebx" + : "=a" (family), "=S" (ext), "=c" (dummy), "=d" (cpu) : "a" (1)); + + familyModel = family; + extFeatures = ext; + return cpu; +} + +struct CPUFlags +{ + bool hasMMX : 1; + bool hasSSE : 1; + bool hasSSE2 : 1; + bool has3DNow : 1; +}; + +static CPUFlags cpuFlags; + +#endif + +void SystemStats::initialiseStats() throw() +{ + static bool initialised = false; + + if (! initialised) + { + initialised = true; + +#if JUCE_MAC + // extremely annoying: adding this line stops the apple menu items from working. Of + // course, not adding it means that carbon windows (e.g. in plugins) won't get + // any events. + //NSApplicationLoad(); + [NSApplication sharedApplication]; +#endif + +#if JUCE_INTEL + { + unsigned int familyModel, extFeatures; + const unsigned int features = getCPUIDWord (familyModel, extFeatures); + + cpuFlags.hasMMX = ((features & (1 << 23)) != 0); + cpuFlags.hasSSE = ((features & (1 << 25)) != 0); + cpuFlags.hasSSE2 = ((features & (1 << 26)) != 0); + cpuFlags.has3DNow = ((extFeatures & (1 << 31)) != 0); + } +#endif + + mach_timebase_info_data_t timebase; + (void) mach_timebase_info (&timebase); + highResTimerFrequency = (int64) (1.0e9 * timebase.denom / timebase.numer); + highResTimerToMillisecRatio = timebase.numer / (1.0e6 * timebase.denom); + + String s (SystemStats::getJUCEVersion()); + + rlimit lim; + getrlimit (RLIMIT_NOFILE, &lim); + lim.rlim_cur = lim.rlim_max = RLIM_INFINITY; + setrlimit (RLIMIT_NOFILE, &lim); + } +} + +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() throw() +{ + return MacOSX; +} + +const String SystemStats::getOperatingSystemName() throw() +{ + return T("Mac OS X"); +} + +bool SystemStats::isOperatingSystem64Bit() throw() +{ +#if JUCE_64BIT + return true; +#else + //xxx not sure how to find this out?.. + return false; +#endif +} + +int SystemStats::getMemorySizeInMegabytes() throw() +{ + uint64 mem = 0; + size_t memSize = sizeof (mem); + int mib[] = { CTL_HW, HW_MEMSIZE }; + sysctl (mib, 2, &mem, &memSize, 0, 0); + return mem / (1024 * 1024); +} + +bool SystemStats::hasMMX() throw() +{ +#if JUCE_INTEL + return cpuFlags.hasMMX; +#else + return false; +#endif +} + +bool SystemStats::hasSSE() throw() +{ +#if JUCE_INTEL + return cpuFlags.hasSSE; +#else + return false; +#endif +} + +bool SystemStats::hasSSE2() throw() +{ +#if JUCE_INTEL + return cpuFlags.hasSSE2; +#else + return false; +#endif +} + +bool SystemStats::has3DNow() throw() +{ +#if JUCE_INTEL + return cpuFlags.has3DNow; +#else + return false; +#endif +} + +const String SystemStats::getCpuVendor() throw() +{ +#if JUCE_INTEL + char v [16]; + juce_getCpuVendor (v); + return String (v, 16); +#else + return String::empty; +#endif +} + +int SystemStats::getCpuSpeedInMegaherz() throw() +{ + uint64 speedHz = 0; + size_t speedSize = sizeof (speedHz); + int mib[] = { CTL_HW, HW_CPU_FREQ }; + sysctl (mib, 2, &speedHz, &speedSize, 0, 0); + +#if JUCE_BIG_ENDIAN + if (speedSize == 4) + speedHz >>= 32; +#endif + return speedHz / 1000000; +} + +int SystemStats::getNumCpus() throw() +{ +#if MACOS_10_4_OR_EARLIER + return MPProcessors(); +#else + return [[NSProcessInfo processInfo] activeProcessorCount]; +#endif +} + +uint32 juce_millisecondsSinceStartup() throw() +{ + return (uint32) (mach_absolute_time() * highResTimerToMillisecRatio); +} + +double Time::getMillisecondCounterHiRes() throw() +{ + return mach_absolute_time() * highResTimerToMillisecRatio; +} + +int64 Time::getHighResolutionTicks() throw() +{ + return (int64) mach_absolute_time(); +} + +int64 Time::getHighResolutionTicksPerSecond() throw() +{ + return highResTimerFrequency; +} + +int64 SystemStats::getClockCycleCounter() throw() +{ + return (int64) mach_absolute_time(); +} + +bool Time::setSystemTimeToThisTime() const throw() +{ + jassertfalse + return false; +} + +int SystemStats::getPageSize() throw() +{ + return NSPageSize(); +} + +void PlatformUtilities::fpuReset() +{ +} + +#endif +/********* End of inlined file: juce_mac_SystemStats.mm *********/ + +/********* Start of inlined file: juce_mac_Network.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() +{ + #ifndef IFT_ETHER + #define IFT_ETHER 6 + #endif + + ifaddrs* addrs = 0; + int numResults = 0; + + if (getifaddrs (&addrs) == 0) + { + const ifaddrs* cursor = addrs; + + while (cursor != 0 && numResults < maxNum) + { + if (cursor->ifa_addr->sa_family == AF_LINK) + { + const sockaddr_dl* const sadd = (const sockaddr_dl*) cursor->ifa_addr; + + if (sadd->sdl_type == IFT_ETHER) + { + const uint8* const addr = ((const uint8*) sadd->sdl_data) + sadd->sdl_nlen; + + uint64 a = 0; + for (int i = 6; --i >= 0;) + a = (a << 8) | addr [littleEndian ? i : (5 - i)]; + + *addresses++ = (int64) a; + ++numResults; + } + } + + cursor = cursor->ifa_next; + } + + freeifaddrs (addrs); + } + + return numResults; +} + +bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, + const String& emailSubject, + const String& bodyText, + const StringArray& filesToAttach) +{ +#if JUCE_IPHONE + //xxx probably need to use MFMailComposeViewController + jassertfalse + return false; +#else + const ScopedAutoReleasePool pool; + + String script; + script << "tell application \"Mail\"\r\n" + "set newMessage to make new outgoing message with properties {subject:\"" + << emailSubject.replace (T("\""), T("\\\"")) + << "\", content:\"" + << bodyText.replace (T("\""), T("\\\"")) + << "\" & return & return}\r\n" + "tell newMessage\r\n" + "set visible to true\r\n" + "set sender to \"sdfsdfsdfewf\"\r\n" + "make new to recipient at end of to recipients with properties {address:\"" + << targetEmailAddress + << "\"}\r\n"; + + for (int i = 0; i < filesToAttach.size(); ++i) + { + script << "tell content\r\n" + "make new attachment with properties {file name:\"" + << filesToAttach[i].replace (T("\""), T("\\\"")) + << "\"} at after the last paragraph\r\n" + "end tell\r\n"; + } + + script << "end tell\r\n" + "end tell\r\n"; + + NSAppleScript* s = [[NSAppleScript alloc] + initWithSource: juceStringToNS (script)]; + NSDictionary* error = 0; + const bool ok = [s executeAndReturnError: &error] != nil; + [s release]; + + return ok; +#endif +} + +END_JUCE_NAMESPACE + +using namespace JUCE_NAMESPACE; + +#define JuceURLConnection MakeObjCClassName(JuceURLConnection) + +@interface JuceURLConnection : NSObject +{ +@public + NSURLRequest* request; + NSURLConnection* connection; + NSMutableData* data; + Thread* runLoopThread; + bool initialised, hasFailed, hasFinished; + int position; + int64 contentLength; + NSLock* dataLock; +} + +- (JuceURLConnection*) initWithRequest: (NSURLRequest*) req withCallback: (URL::OpenStreamProgressCallback*) callback withContext: (void*) context; +- (void) dealloc; +- (void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) response; +- (void) connection: (NSURLConnection*) connection didFailWithError: (NSError*) error; +- (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) data; +- (void) connectionDidFinishLoading: (NSURLConnection*) connection; + +- (BOOL) isOpen; +- (int) read: (char*) dest numBytes: (int) num; +- (int) readPosition; +- (void) stop; +- (void) createConnection; + +@end + +class JuceURLConnectionMessageThread : public Thread +{ + JuceURLConnection* owner; + +public: + JuceURLConnectionMessageThread (JuceURLConnection* owner_) + : Thread (T("http connection")), + owner (owner_) + { + } + + ~JuceURLConnectionMessageThread() + { + stopThread (10000); + } + + void run() + { + [owner createConnection]; + + while (! threadShouldExit()) + { + const ScopedAutoReleasePool pool; + [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; + } + } +}; + +@implementation JuceURLConnection + +- (JuceURLConnection*) initWithRequest: (NSURLRequest*) req + withCallback: (URL::OpenStreamProgressCallback*) callback + withContext: (void*) context; +{ + [super init]; + request = req; + [request retain]; + data = [[NSMutableData data] retain]; + dataLock = [[NSLock alloc] init]; + connection = 0; + initialised = false; + hasFailed = false; + hasFinished = false; + contentLength = -1; + + runLoopThread = new JuceURLConnectionMessageThread (self); + runLoopThread->startThread(); + + while (runLoopThread->isThreadRunning() && ! initialised) + { + if (callback != 0) + callback (context, -1, [[request HTTPBody] length]); + + Thread::sleep (1); + } + + return self; +} + +- (void) dealloc +{ + [self stop]; + + delete runLoopThread; + [connection release]; + [data release]; + [dataLock release]; + [request release]; + [super dealloc]; +} + +- (void) createConnection +{ + connection = [[NSURLConnection alloc] initWithRequest: request + delegate: [self retain]]; + + if (connection == nil) + runLoopThread->signalThreadShouldExit(); +} + +- (void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) response +{ + [dataLock lock]; + [data setLength: 0]; + [dataLock unlock]; + initialised = true; + contentLength = [response expectedContentLength]; +} + +- (void) connection: (NSURLConnection*) connection didFailWithError: (NSError*) error +{ + DBG (nsStringToJuce ([error description])); + hasFailed = true; + initialised = true; + runLoopThread->signalThreadShouldExit(); +} + +- (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) newData +{ + [dataLock lock]; + [data appendData: newData]; + [dataLock unlock]; + initialised = true; +} + +- (void) connectionDidFinishLoading: (NSURLConnection*) connection +{ + hasFinished = true; + initialised = true; + runLoopThread->signalThreadShouldExit(); +} + +- (BOOL) isOpen +{ + return connection != 0 && ! hasFailed; +} + +- (int) readPosition +{ + return position; +} + +- (int) read: (char*) dest numBytes: (int) numNeeded +{ + int numDone = 0; + + while (numNeeded > 0) + { + int available = jmin (numNeeded, [data length]); + + if (available > 0) + { + [dataLock lock]; + [data getBytes: dest length: available]; + [data replaceBytesInRange: NSMakeRange (0, available) withBytes: nil length: 0]; + [dataLock unlock]; + + numDone += available; + numNeeded -= available; + dest += available; + } + else + { + if (hasFailed || hasFinished) + break; + + Thread::sleep (1); + } + } + + position += numDone; + return numDone; +} + +- (void) stop +{ + [connection cancel]; + runLoopThread->stopThread (10000); +} + +@end +BEGIN_JUCE_NAMESPACE + +bool juce_isOnLine() +{ + return true; +} + +void* juce_openInternetFile (const String& url, + const String& headers, + const MemoryBlock& postData, + const bool isPost, + URL::OpenStreamProgressCallback* callback, + void* callbackContext, + int timeOutMs) +{ + const ScopedAutoReleasePool pool; + + NSMutableURLRequest* req = [NSMutableURLRequest + requestWithURL: [NSURL URLWithString: juceStringToNS (url)] + cachePolicy: NSURLRequestUseProtocolCachePolicy + timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)]; + + if (req == nil) + return 0; + + [req setHTTPMethod: isPost ? @"POST" : @"GET"]; + //[req setCachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; + + StringArray headerLines; + headerLines.addLines (headers); + headerLines.removeEmptyStrings (true); + + for (int i = 0; i < headerLines.size(); ++i) + { + const String key (headerLines[i].upToFirstOccurrenceOf (T(":"), false, false).trim()); + const String value (headerLines[i].fromFirstOccurrenceOf (T(":"), false, false).trim()); + + if (key.isNotEmpty() && value.isNotEmpty()) + [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; + } + + if (isPost && postData.getSize() > 0) + { + [req setHTTPBody: [NSData dataWithBytes: postData.getData() + length: postData.getSize()]]; + } + + JuceURLConnection* const s = [[JuceURLConnection alloc] initWithRequest: req + withCallback: callback + withContext: callbackContext]; + + if ([s isOpen]) + return s; + + [s release]; + return 0; +} + +void juce_closeInternetFile (void* handle) +{ + JuceURLConnection* const s = (JuceURLConnection*) handle; + + if (s != 0) + { + const ScopedAutoReleasePool pool; + [s stop]; + [s release]; + } +} + +int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) +{ + JuceURLConnection* const s = (JuceURLConnection*) handle; + + if (s != 0) + { + const ScopedAutoReleasePool pool; + return [s read: (char*) buffer numBytes: bytesToRead]; + } + + return 0; +} + +int64 juce_getInternetFileContentLength (void* handle) +{ + JuceURLConnection* const s = (JuceURLConnection*) handle; + + if (s != 0) + return s->contentLength; + + return -1; +} + +int juce_seekInInternetFile (void* handle, int newPosition) +{ + JuceURLConnection* const s = (JuceURLConnection*) handle; + + if (s != 0) + return [s readPosition]; + + return 0; +} + +#endif +/********* End of inlined file: juce_mac_Network.mm *********/ + +/********* Start of inlined file: juce_posix_NamedPipe.cpp *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +struct NamedPipeInternal +{ + String pipeInName, pipeOutName; + int pipeIn, pipeOut; + + bool volatile createdPipe, blocked, stopReadOperation; + + static void signalHandler (int) {} +}; + +void NamedPipe::cancelPendingReads() +{ + while (internal != 0 && ((NamedPipeInternal*) internal)->blocked) + { + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + intern->stopReadOperation = true; + + char buffer [1] = { 0 }; + int bytesWritten = ::write (intern->pipeIn, buffer, 1); + (void) bytesWritten; + + int timeout = 2000; + while (intern->blocked && --timeout >= 0) + Thread::sleep (2); + + intern->stopReadOperation = false; + } +} + +void NamedPipe::close() +{ + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + if (intern != 0) + { + internal = 0; + + if (intern->pipeIn != -1) + ::close (intern->pipeIn); + + if (intern->pipeOut != -1) + ::close (intern->pipeOut); + + if (intern->createdPipe) + { + unlink (intern->pipeInName); + unlink (intern->pipeOutName); + } + + delete intern; + } +} + +bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) +{ + close(); + + NamedPipeInternal* const intern = new NamedPipeInternal(); + internal = intern; + intern->createdPipe = createPipe; + intern->blocked = false; + intern->stopReadOperation = false; + + signal (SIGPIPE, NamedPipeInternal::signalHandler); + siginterrupt (SIGPIPE, 1); + + const String pipePath (T("/tmp/") + File::createLegalFileName (pipeName)); + + intern->pipeInName = pipePath + T("_in"); + intern->pipeOutName = pipePath + T("_out"); + intern->pipeIn = -1; + intern->pipeOut = -1; + + if (createPipe) + { + if ((mkfifo (intern->pipeInName, 0666) && errno != EEXIST) + || (mkfifo (intern->pipeOutName, 0666) && errno != EEXIST)) + { + delete intern; + internal = 0; + + return false; + } + } + + return true; +} + +int NamedPipe::read (void* destBuffer, int maxBytesToRead, int /*timeOutMilliseconds*/) +{ + int bytesRead = -1; + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + if (intern != 0) + { + intern->blocked = true; + + if (intern->pipeIn == -1) + { + if (intern->createdPipe) + intern->pipeIn = ::open (intern->pipeInName, O_RDWR); + else + intern->pipeIn = ::open (intern->pipeOutName, O_RDWR); + + if (intern->pipeIn == -1) + { + intern->blocked = false; + return -1; + } + } + + bytesRead = 0; + + char* p = (char*) destBuffer; + + while (bytesRead < maxBytesToRead) + { + const int bytesThisTime = maxBytesToRead - bytesRead; + const int numRead = ::read (intern->pipeIn, p, bytesThisTime); + + if (numRead <= 0 || intern->stopReadOperation) + { + bytesRead = -1; + break; + } + + bytesRead += numRead; + p += bytesRead; + } + + intern->blocked = false; + } + + return bytesRead; +} + +int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) +{ + int bytesWritten = -1; + NamedPipeInternal* const intern = (NamedPipeInternal*) internal; + + if (intern != 0) + { + if (intern->pipeOut == -1) + { + if (intern->createdPipe) + intern->pipeOut = ::open (intern->pipeOutName, O_WRONLY); + else + intern->pipeOut = ::open (intern->pipeInName, O_WRONLY); + + if (intern->pipeOut == -1) + { + return -1; + } + } + + const char* p = (const char*) sourceBuffer; + bytesWritten = 0; + + const uint32 timeOutTime = Time::getMillisecondCounter() + timeOutMilliseconds; + + while (bytesWritten < numBytesToWrite + && (timeOutMilliseconds < 0 || Time::getMillisecondCounter() < timeOutTime)) + { + const int bytesThisTime = numBytesToWrite - bytesWritten; + const int numWritten = ::write (intern->pipeOut, p, bytesThisTime); + + if (numWritten <= 0) + { + bytesWritten = -1; + break; + } + + bytesWritten += numWritten; + p += bytesWritten; + } + } + + return bytesWritten; +} + +#endif +/********* End of inlined file: juce_posix_NamedPipe.cpp *********/ + +/********* Start of inlined file: juce_mac_Threads.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +/* + Note that a lot of methods that you'd expect to find in this file actually + live in juce_posix_SharedCode.h! +*/ + +void JUCE_API juce_threadEntryPoint (void*); + +void* threadEntryProc (void* userData) throw() +{ + const ScopedAutoReleasePool pool; + juce_threadEntryPoint (userData); + return 0; +} + +void* juce_createThread (void* userData) throw() +{ + pthread_t handle = 0; + + if (pthread_create (&handle, 0, threadEntryProc, userData) == 0) + { + pthread_detach (handle); + return (void*) handle; + } + + return 0; +} + +void juce_killThread (void* handle) throw() +{ + if (handle != 0) + pthread_cancel ((pthread_t) handle); +} + +void juce_setCurrentThreadName (const String& /*name*/) throw() +{ +} + +Thread::ThreadID Thread::getCurrentThreadId() throw() +{ + return (ThreadID) pthread_self(); +} + +bool juce_setThreadPriority (void* handle, int priority) throw() +{ + if (handle == 0) + handle = (void*) pthread_self(); + + struct sched_param param; + int policy; + pthread_getschedparam ((pthread_t) handle, &policy, ¶m); + param.sched_priority = jlimit (1, 127, 1 + (priority * 126) / 11); + return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0; +} + +void Thread::yield() throw() +{ + sched_yield(); +} + +void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() +{ + // xxx + jassertfalse +} + +bool Process::isForegroundProcess() throw() +{ +#if JUCE_MAC + return [NSApp isActive]; +#else + return true; // xxx change this if more than one app is ever possible on the iPhone! +#endif +} + +void Process::raisePrivilege() +{ + jassertfalse +} + +void Process::lowerPrivilege() +{ + jassertfalse +} + +void Process::terminate() +{ + exit (0); +} + +void Process::setPriority (ProcessPriority p) +{ + // xxx +} + +#endif +/********* End of inlined file: juce_mac_Threads.mm *********/ + +/********* Start of inlined file: juce_posix_SharedCode.h *********/ +/* + This file contains posix routines that are common to both the Linux and Mac builds. + + It gets included directly in the cpp files for these platforms. +*/ + +CriticalSection::CriticalSection() throw() +{ + pthread_mutexattr_t atts; + pthread_mutexattr_init (&atts); + pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init (&internal, &atts); +} + +CriticalSection::~CriticalSection() throw() +{ + pthread_mutex_destroy (&internal); +} + +void CriticalSection::enter() const throw() +{ + pthread_mutex_lock (&internal); +} + +bool CriticalSection::tryEnter() const throw() +{ + return pthread_mutex_trylock (&internal) == 0; +} + +void CriticalSection::exit() const throw() +{ + pthread_mutex_unlock (&internal); +} + +struct EventStruct +{ + pthread_cond_t condition; + pthread_mutex_t mutex; + bool triggered; +}; + +WaitableEvent::WaitableEvent() throw() +{ + EventStruct* const es = new EventStruct(); + es->triggered = false; + + pthread_cond_init (&es->condition, 0); + pthread_mutex_init (&es->mutex, 0); + + internal = es; +} + +WaitableEvent::~WaitableEvent() throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_cond_destroy (&es->condition); + pthread_mutex_destroy (&es->mutex); + + delete es; +} + +bool WaitableEvent::wait (const int timeOutMillisecs) const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + bool ok = true; + pthread_mutex_lock (&es->mutex); + + if (timeOutMillisecs < 0) + { + while (! es->triggered) + pthread_cond_wait (&es->condition, &es->mutex); + } + else + { + while (! es->triggered) + { + struct timeval t; + gettimeofday (&t, 0); + + struct timespec time; + time.tv_sec = t.tv_sec + (timeOutMillisecs / 1000); + time.tv_nsec = (t.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000; + + if (time.tv_nsec >= 1000000000) + { + time.tv_nsec -= 1000000000; + time.tv_sec++; + } + + if (pthread_cond_timedwait (&es->condition, &es->mutex, &time) == ETIMEDOUT) + { + ok = false; + break; + } + } + } + + es->triggered = false; + + pthread_mutex_unlock (&es->mutex); + return ok; +} + +void WaitableEvent::signal() const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_mutex_lock (&es->mutex); + es->triggered = true; + pthread_cond_broadcast (&es->condition); + pthread_mutex_unlock (&es->mutex); +} + +void WaitableEvent::reset() const throw() +{ + EventStruct* const es = (EventStruct*) internal; + + pthread_mutex_lock (&es->mutex); + es->triggered = false; + pthread_mutex_unlock (&es->mutex); +} + +void JUCE_CALLTYPE Thread::sleep (int millisecs) throw() +{ + struct timespec time; + time.tv_sec = millisecs / 1000; + time.tv_nsec = (millisecs % 1000) * 1000000; + nanosleep (&time, 0); +} + +const tchar File::separator = T('/'); +const tchar* File::separatorString = T("/"); + +bool juce_copyFile (const String& s, const String& d) throw(); + +static bool juce_stat (const String& fileName, struct stat& info) throw() +{ + return fileName.isNotEmpty() + && (stat (fileName.toUTF8(), &info) == 0); +} + +bool juce_isDirectory (const String& fileName) throw() +{ + struct stat info; + + return fileName.isEmpty() + || (juce_stat (fileName, info) + && ((info.st_mode & S_IFDIR) != 0)); +} + +bool juce_fileExists (const String& fileName, const bool dontCountDirectories) throw() +{ + if (fileName.isEmpty()) + return false; + + const char* const fileNameUTF8 = fileName.toUTF8(); + bool exists = access (fileNameUTF8, F_OK) == 0; + + if (exists && dontCountDirectories) + { + struct stat info; + const int res = stat (fileNameUTF8, &info); + + if (res == 0 && (info.st_mode & S_IFDIR) != 0) + exists = false; + } + + return exists; +} + +int64 juce_getFileSize (const String& fileName) throw() +{ + struct stat info; + return juce_stat (fileName, info) ? info.st_size : 0; +} + +bool juce_canWriteToFile (const String& fileName) throw() +{ + return access (fileName.toUTF8(), W_OK) == 0; +} + +bool juce_deleteFile (const String& fileName) throw() +{ + if (juce_isDirectory (fileName)) + return rmdir ((const char*) fileName.toUTF8()) == 0; + else + return remove ((const char*) fileName.toUTF8()) == 0; +} + +bool juce_moveFile (const String& source, const String& dest) throw() +{ + if (rename (source.toUTF8(), dest.toUTF8()) == 0) + return true; + + if (juce_canWriteToFile (source) + && juce_copyFile (source, dest)) + { + if (juce_deleteFile (source)) + return true; + + juce_deleteFile (dest); + } + + return false; +} + +void juce_createDirectory (const String& fileName) throw() +{ + mkdir (fileName.toUTF8(), 0777); +} + +void* juce_fileOpen (const String& fileName, bool forWriting) throw() +{ + int flags = O_RDONLY; + + if (forWriting) + { + if (juce_fileExists (fileName, false)) + { + const int f = open ((const char*) fileName.toUTF8(), O_RDWR, 00644); + + if (f != -1) + lseek (f, 0, SEEK_END); + + return (void*) f; + } + else + { + flags = O_RDWR + O_CREAT; + } + } + + return (void*) open ((const char*) fileName.toUTF8(), flags, 00644); +} + +void juce_fileClose (void* handle) throw() +{ + if (handle != 0) + close ((int) (pointer_sized_int) handle); +} + +int juce_fileRead (void* handle, void* buffer, int size) throw() +{ + if (handle != 0) + return read ((int) (pointer_sized_int) handle, buffer, size); + + return 0; +} + +int juce_fileWrite (void* handle, const void* buffer, int size) throw() +{ + if (handle != 0) + return write ((int) (pointer_sized_int) handle, buffer, size); + + return 0; +} + +int64 juce_fileSetPosition (void* handle, int64 pos) throw() +{ + if (handle != 0 && lseek ((int) (pointer_sized_int) handle, pos, SEEK_SET) == pos) + return pos; + + return -1; +} + +int64 juce_fileGetPosition (void* handle) throw() +{ + if (handle != 0) + return lseek ((int) (pointer_sized_int) handle, 0, SEEK_CUR); + else + return -1; +} + +void juce_fileFlush (void* handle) throw() +{ + if (handle != 0) + fsync ((int) (pointer_sized_int) handle); +} + +const File juce_getExecutableFile() +{ + Dl_info exeInfo; + dladdr ((const void*) juce_getExecutableFile, &exeInfo); + return File (exeInfo.dli_fname); +} + +// if this file doesn't exist, find a parent of it that does.. +static bool doStatFS (const File* file, struct statfs& result) throw() +{ + File f (*file); + + for (int i = 5; --i >= 0;) + { + if (f.exists()) + break; + + f = f.getParentDirectory(); + } + + return statfs (f.getFullPathName().toUTF8(), &result) == 0; +} + +int64 File::getBytesFreeOnVolume() const throw() +{ + struct statfs buf; + if (doStatFS (this, buf)) + return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user + + return 0; +} + +int64 File::getVolumeTotalSize() const throw() +{ + struct statfs buf; + if (doStatFS (this, buf)) + return (int64) buf.f_bsize * (int64) buf.f_blocks; + + return 0; +} + +const String juce_getVolumeLabel (const String& filenameOnVolume, + int& volumeSerialNumber) throw() +{ + volumeSerialNumber = 0; + +#if JUCE_MAC + struct VolAttrBuf + { + u_int32_t length; + attrreference_t mountPointRef; + char mountPointSpace [MAXPATHLEN]; + } attrBuf; + + struct attrlist attrList; + zerostruct (attrList); + attrList.bitmapcount = ATTR_BIT_MAP_COUNT; + attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME; + + File f (filenameOnVolume); + + for (;;) + { + if (getattrlist ((const char*) f.getFullPathName().toUTF8(), + &attrList, &attrBuf, sizeof(attrBuf), 0) == 0) + { + return String::fromUTF8 (((const uint8*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset, + (int) attrBuf.mountPointRef.attr_length); + } + + const File parent (f.getParentDirectory()); + + if (f == parent) + break; + + f = parent; + } +#endif + + return String::empty; +} + +void juce_runSystemCommand (const String& command) +{ + int result = system ((const char*) command.toUTF8()); + (void) result; +} + +const String juce_getOutputFromCommand (const String& command) +{ + // slight bodge here, as we just pipe the output into a temp file and read it... + const File tempFile (File::getSpecialLocation (File::tempDirectory) + .getNonexistentChildFile (String::toHexString (Random::getSystemRandom().nextInt()), ".tmp", false)); + + juce_runSystemCommand (command + " > " + tempFile.getFullPathName()); + + String result (tempFile.loadFileAsString()); + tempFile.deleteFile(); + return result; +} + +#if JUCE_64BIT + #define filedesc ((long long) internal) +#else + #define filedesc ((int) internal) +#endif + +InterProcessLock::InterProcessLock (const String& name_) throw() + : internal (0), + name (name_), + reentrancyLevel (0) +{ +#if JUCE_MAC + // (don't use getSpecialLocation() to avoid the temp folder being different for each app) + const File temp (File (T("~/Library/Caches/Juce")).getChildFile (name)); +#else + const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name)); +#endif + + temp.create(); + + internal = (void*) open (temp.getFullPathName().toUTF8(), O_RDWR); +} + +InterProcessLock::~InterProcessLock() throw() +{ + while (reentrancyLevel > 0) + this->exit(); + + close (filedesc); +} + +bool InterProcessLock::enter (const int timeOutMillisecs) throw() +{ + if (internal == 0) + return false; + + if (reentrancyLevel != 0) + return true; + + const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs; + + struct flock fl; + zerostruct (fl); + fl.l_whence = SEEK_SET; + fl.l_type = F_WRLCK; + + for (;;) + { + const int result = fcntl (filedesc, F_SETLK, &fl); + + if (result >= 0) + { + ++reentrancyLevel; + return true; + } + + if (errno != EINTR) + { + if (timeOutMillisecs == 0 + || (timeOutMillisecs > 0 && Time::currentTimeMillis() >= endTime)) + break; + + Thread::sleep (10); + } + } + + return false; +} + +void InterProcessLock::exit() throw() +{ + if (reentrancyLevel > 0 && internal != 0) + { + --reentrancyLevel; + + struct flock fl; + zerostruct (fl); + fl.l_whence = SEEK_SET; + fl.l_type = F_UNLCK; + + for (;;) + { + const int result = fcntl (filedesc, F_SETLKW, &fl); + + if (result >= 0 || errno != EINTR) + break; + } + } +} +/********* End of inlined file: juce_posix_SharedCode.h *********/ + +/********* Start of inlined file: juce_mac_Files.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +/* + Note that a lot of methods that you'd expect to find in this file actually + live in juce_posix_SharedCode.h! +*/ + +void juce_getFileTimes (const String& fileName, + int64& modificationTime, + int64& accessTime, + int64& creationTime) throw() +{ + modificationTime = 0; + accessTime = 0; + creationTime = 0; + + struct stat info; + const int res = stat (fileName.toUTF8(), &info); + if (res == 0) + { + modificationTime = (int64) info.st_mtime * 1000; + accessTime = (int64) info.st_atime * 1000; + creationTime = (int64) info.st_ctime * 1000; + } +} + +bool juce_setFileTimes (const String& fileName, + int64 modificationTime, + int64 accessTime, + int64 creationTime) throw() +{ + struct utimbuf times; + times.actime = (time_t) (accessTime / 1000); + times.modtime = (time_t) (modificationTime / 1000); + + return utime (fileName.toUTF8(), ×) == 0; +} + +bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) throw() +{ + struct stat info; + const int res = stat (fileName.toUTF8(), &info); + if (res != 0) + return false; + + info.st_mode &= 0777; // Just permissions + + if (isReadOnly) + info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + else + // Give everybody write permission? + info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + + return chmod (fileName.toUTF8(), info.st_mode) == 0; +} + +bool juce_copyFile (const String& src, const String& dst) throw() +{ + const ScopedAutoReleasePool pool; + NSFileManager* fm = [NSFileManager defaultManager]; + + return [fm fileExistsAtPath: juceStringToNS (src)] +#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + && [fm copyItemAtPath: juceStringToNS (src) + toPath: juceStringToNS (dst) + error: nil]; +#else + && [fm copyPath: juceStringToNS (src) + toPath: juceStringToNS (dst) + handler: nil]; +#endif +} + +const StringArray juce_getFileSystemRoots() throw() +{ + StringArray s; + s.add (T("/")); + return s; +} + +static bool isFileOnDriveType (const File* const f, const char** types) throw() +{ + struct statfs buf; + + if (doStatFS (f, buf)) + { + const String type (buf.f_fstypename); + + while (*types != 0) + if (type.equalsIgnoreCase (*types++)) + return true; + } + + return false; +} + +bool File::isOnCDRomDrive() const throw() +{ + static const char* const cdTypes[] = { "cd9660", "cdfs", "cddafs", "udf", 0 }; + + return isFileOnDriveType (this, (const char**) cdTypes); +} + +bool File::isOnHardDisk() const throw() +{ + static const char* const nonHDTypes[] = { "nfs", "smbfs", "ramfs", 0 }; + + return ! (isOnCDRomDrive() || isFileOnDriveType (this, (const char**) nonHDTypes)); +} + +bool File::isOnRemovableDrive() const throw() +{ +#if JUCE_IPHONE + return false; // xxx is this possible? +#else + const ScopedAutoReleasePool pool; + BOOL removable = false; + + [[NSWorkspace sharedWorkspace] + getFileSystemInfoForPath: juceStringToNS (getFullPathName()) + isRemovable: &removable + isWritable: nil + isUnmountable: nil + description: nil + type: nil]; + + return removable; +#endif +} + +static bool juce_isHiddenFile (const String& path) throw() +{ +#if JUCE_IPHONE + return File (path).getFileName().startsWithChar (T('.')); +#else + FSRef ref; + if (! PlatformUtilities::makeFSRefFromPath (&ref, path)) + return false; + + FSCatalogInfo info; + FSGetCatalogInfo (&ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &info, 0, 0, 0); + + if ((info.nodeFlags & kFSNodeIsDirectoryBit) != 0) + return (((FolderInfo*) &info.finderInfo)->finderFlags & kIsInvisible) != 0; + + return (((FileInfo*) &info.finderInfo)->finderFlags & kIsInvisible) != 0; +#endif +} + +bool File::isHidden() const throw() +{ + return juce_isHiddenFile (getFullPathName()); +} + +const File File::getSpecialLocation (const SpecialLocationType type) +{ + const ScopedAutoReleasePool pool; + + String resultPath; + + switch (type) + { + case userHomeDirectory: + resultPath = nsStringToJuce (NSHomeDirectory()); + break; + + case userDocumentsDirectory: + resultPath = "~/Documents"; + break; + + case userDesktopDirectory: + resultPath = "~/Desktop"; + break; + + case userApplicationDataDirectory: + resultPath = "~/Library"; + break; + + case commonApplicationDataDirectory: + resultPath = "/Library"; + break; + + case globalApplicationsDirectory: + resultPath = "/Applications"; + break; + + case userMusicDirectory: + resultPath = "~/Music"; + break; + + case userMoviesDirectory: + resultPath = "~/Movies"; + break; + + case tempDirectory: + { + File tmp (T("~/Library/Caches/") + juce_getExecutableFile().getFileNameWithoutExtension()); + + tmp.createDirectory(); + return tmp.getFullPathName(); + } + + case currentExecutableFile: + return juce_getExecutableFile(); + + case currentApplicationFile: + { + const File exe (juce_getExecutableFile()); + const File parent (exe.getParentDirectory()); + + return parent.getFullPathName().endsWithIgnoreCase (T("Contents/MacOS")) + ? parent.getParentDirectory().getParentDirectory() + : exe; + } + + default: + jassertfalse // unknown type? + break; + } + + if (resultPath != 0) + return File (PlatformUtilities::convertToPrecomposedUnicode (resultPath)); + + return File::nonexistent; +} + +const File File::getCurrentWorkingDirectory() throw() +{ + char buf [2048]; + getcwd (buf, sizeof(buf)); + + return File (PlatformUtilities::convertToPrecomposedUnicode (buf)); +} + +bool File::setAsCurrentWorkingDirectory() const throw() +{ + return chdir (getFullPathName().toUTF8()) == 0; +} + +const String File::getVersion() const throw() +{ + const ScopedAutoReleasePool pool; + String result; + + NSBundle* bundle = [NSBundle bundleWithPath: juceStringToNS (getFullPathName())]; + + if (bundle != 0) + { + NSDictionary* info = [bundle infoDictionary]; + + if (info != 0) + { + NSString* name = [info valueForKey: @"CFBundleShortVersionString"]; + + if (name != nil) + result = nsStringToJuce (name); + } + } + + return result; +} + +const File File::getLinkedTarget() const throw() +{ + NSString* dest = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath: juceStringToNS (getFullPathName()) error: nil]; + + if (dest != nil) + return File (nsStringToJuce (dest)); + + return *this; +} + +bool File::moveToTrash() const throw() +{ + if (! exists()) + return true; + +#if JUCE_IPHONE + return deleteFile(); //xxx is there a trashcan on the iPhone? +#else + const ScopedAutoReleasePool pool; + + NSString* p = juceStringToNS (getFullPathName()); + + return [[NSWorkspace sharedWorkspace] + performFileOperation: NSWorkspaceRecycleOperation + source: [p stringByDeletingLastPathComponent] + destination: @"" + files: [NSArray arrayWithObject: [p lastPathComponent]] + tag: nil ]; +#endif +} + +struct FindFileStruct +{ + NSDirectoryEnumerator* enumerator; + String parentDir; +}; + +void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, + bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, + Time* creationTime, bool* isReadOnly) throw() +{ + NSDirectoryEnumerator* e = [[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory)]; + + if (e != 0) + { + FindFileStruct* ff = new FindFileStruct(); + ff->enumerator = [e retain]; + ff->parentDir = directory; + + if (! ff->parentDir.endsWithChar (File::separator)) + ff->parentDir += File::separator; + + if (juce_findFileNext (ff, firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly)) + return ff; + + [e release]; + delete ff; + } + + return 0; +} + +bool juce_findFileNext (void* handle, String& resultFile, + bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) throw() +{ + FindFileStruct* ff = (FindFileStruct*) handle; + NSString* file; + + if (ff == 0 || (file = [ff->enumerator nextObject]) == 0) + return false; + + [ff->enumerator skipDescendents]; + resultFile = nsStringToJuce (file); + + const String path (ff->parentDir + resultFile); + + if (isDir != 0 || fileSize != 0) + { + struct stat info; + const bool statOk = juce_stat (path, info); + + if (isDir != 0) + *isDir = statOk && ((info.st_mode & S_IFDIR) != 0); + + if (isHidden != 0) + *isHidden = juce_isHiddenFile (path); + + if (fileSize != 0) + *fileSize = statOk ? info.st_size : 0; + } + + if (modTime != 0 || creationTime != 0) + { + int64 m, a, c; + juce_getFileTimes (path, m, a, c); + + if (modTime != 0) + *modTime = m; + + if (creationTime != 0) + *creationTime = c; + } + + if (isReadOnly != 0) + *isReadOnly = ! juce_canWriteToFile (path); + + return true; +} + +void juce_findFileClose (void* handle) throw() +{ + FindFileStruct* ff = (FindFileStruct*) handle; + [ff->enumerator release]; + delete ff; +} + +bool juce_launchExecutable (const String& pathAndArguments) throw() +{ + const char* const argv[4] = { "/bin/sh", "-c", (const char*) pathAndArguments, 0 }; + + const int cpid = fork(); + + if (cpid == 0) + { + // Child process + if (execve (argv[0], (char**) argv, 0) < 0) + exit (0); + } + else + { + if (cpid < 0) + return false; + } + + return true; +} + +bool juce_launchFile (const String& fileName, + const String& parameters) throw() +{ +#if JUCE_IPHONE + return false; // is this possible? +#else + const ScopedAutoReleasePool pool; + + if (parameters.isEmpty()) + { + return [[NSWorkspace sharedWorkspace] openFile: juceStringToNS (fileName)] + || [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: juceStringToNS (fileName)]]; + } + + bool ok = false; + + FSRef ref; + if (PlatformUtilities::makeFSRefFromPath (&ref, fileName)) + { + if (PlatformUtilities::isBundle (fileName)) + { + NSMutableArray* urls = [NSMutableArray array]; + + StringArray docs; + docs.addTokens (parameters, true); + for (int i = 0; i < docs.size(); ++i) + [urls addObject: juceStringToNS (docs[i])]; + + ok = [[NSWorkspace sharedWorkspace] openURLs: urls + withAppBundleIdentifier: [[NSBundle bundleWithPath: juceStringToNS (fileName)] bundleIdentifier] + options: nil + additionalEventParamDescriptor: nil + launchIdentifiers: nil]; + } + else + { + ok = juce_launchExecutable (T("\"") + fileName + T("\" ") + parameters); + } + } + + return ok; +#endif +} + +#if ! JUCE_IPHONE +bool PlatformUtilities::makeFSRefFromPath (FSRef* destFSRef, const String& path) +{ + return FSPathMakeRef ((const UInt8*) path.toUTF8(), destFSRef, 0) == noErr; +} + +const String PlatformUtilities::makePathFromFSRef (FSRef* file) +{ + uint8 path [2048]; + zeromem (path, sizeof (path)); + + String result; + + if (FSRefMakePath (file, (UInt8*) path, sizeof (path) - 1) == noErr) + result = String::fromUTF8 (path); + + return PlatformUtilities::convertToPrecomposedUnicode (result); +} +#endif + +OSType PlatformUtilities::getTypeOfFile (const String& filename) +{ + const ScopedAutoReleasePool pool; + NSDictionary* fileDict = [[NSFileManager defaultManager] fileAttributesAtPath: juceStringToNS (filename) traverseLink: NO]; + return (OSType) [fileDict objectForKey: NSFileHFSTypeCode]; +} + +bool PlatformUtilities::isBundle (const String& filename) +{ +#if JUCE_IPHONE + return false; // xxx can't find a sensible way to do this without trying to open the bundle.. +#else + const ScopedAutoReleasePool pool; + return [[NSWorkspace sharedWorkspace] isFilePackageAtPath: juceStringToNS (filename)]; +#endif +} + +#endif +/********* End of inlined file: juce_mac_Files.mm *********/ + +/********* Start of inlined file: juce_iphone_MiscUtilities.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +ScopedAutoReleasePool::ScopedAutoReleasePool() +{ + pool = [[NSAutoreleasePool alloc] init]; +} + +ScopedAutoReleasePool::~ScopedAutoReleasePool() +{ + [((NSAutoreleasePool*) pool) release]; +} + +void PlatformUtilities::beep() +{ + //xxx + //AudioServicesPlaySystemSound (); +} + +void PlatformUtilities::addItemToDock (const File& file) +{ +} + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + +bool AlertWindow::showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel) +{ + const ScopedAutoReleasePool pool; + + UIAlertView *alert = [[[UIAlertView alloc] initWithTitle: juceStringToNS (title) + message: juceStringToNS (title) + delegate: nil + cancelButtonTitle: @"OK" + otherButtonTitles: (isOkCancel ? @"Cancel" : nil), nil] autorelease]; + alert.cancelButtonIndex = alert.firstOtherButtonIndex; + [alert show]; + + // xxx need to use a delegate to find which button was clicked + return false; +} + +bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) +{ + jassertfalse // not implemented! + return false; +} + +bool DragAndDropContainer::performExternalDragDropOfText (const String& text) +{ + jassertfalse // not implemented! + return false; +} + +bool Desktop::canUseSemiTransparentWindows() throw() +{ + return true; +} + +void Desktop::getMousePosition (int& x, int& y) throw() +{ + x = 0; + y = 0; +} + +void Desktop::setMousePosition (int x, int y) throw() +{ +} + +void Desktop::setScreenSaverEnabled (const bool isEnabled) throw() +{ + [[UIApplication sharedApplication] setIdleTimerDisabled: ! isEnabled]; +} + +bool Desktop::isScreenSaverEnabled() throw() +{ + return ! [[UIApplication sharedApplication] isIdleTimerDisabled]; +} + +void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool clipToWorkArea) throw() +{ + const ScopedAutoReleasePool pool; + monitorCoords.clear(); + + CGRect r = clipToWorkArea ? [[UIScreen mainScreen] applicationFrame] + : [[UIScreen mainScreen] bounds]; + + monitorCoords.add (Rectangle ((int) r.origin.x, + (int) r.origin.y, + (int) r.size.width, + (int) r.size.height)); +} + +#endif + +#endif +/********* End of inlined file: juce_iphone_MiscUtilities.mm *********/ + +/********* Start of inlined file: juce_mac_Debugging.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +void Logger::outputDebugString (const String& text) throw() +{ + fputs (text.toUTF8(), stderr); + fputs ("\n", stderr); +} + +void Logger::outputDebugPrintf (const tchar* format, ...) throw() +{ + String text; + va_list args; + va_start (args, format); + text.vprintf (format, args); + outputDebugString (text); +} + +bool JUCE_CALLTYPE juce_isRunningUnderDebugger() throw() +{ + static char testResult = 0; + + if (testResult == 0) + { + struct kinfo_proc info; + int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; + size_t sz = sizeof (info); + sysctl (m, 4, &info, &sz, 0, 0); + testResult = ((info.kp_proc.p_flag & P_TRACED) != 0) ? 1 : -1; + } + + return testResult > 0; +} + +bool JUCE_CALLTYPE Process::isRunningUnderDebugger() throw() +{ + return juce_isRunningUnderDebugger(); +} + +#endif +/********* End of inlined file: juce_mac_Debugging.mm *********/ + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + /*#include "mac/juce_mac_NSViewComponentPeer.mm" + +/********* Start of inlined file: juce_mac_MouseCursor.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +static NSImage* juceImageToNSImage (const Image& image) +{ + const ScopedAutoReleasePool pool; + int lineStride, pixelStride; + const uint8* pixels = image.lockPixelDataReadOnly (0, 0, image.getWidth(), image.getHeight(), + lineStride, pixelStride); + + NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes: NULL + pixelsWide: image.getWidth() + pixelsHigh: image.getHeight() + bitsPerSample: 8 + samplesPerPixel: image.hasAlphaChannel() ? 4 : 3 + hasAlpha: image.hasAlphaChannel() + isPlanar: NO + colorSpaceName: NSCalibratedRGBColorSpace + bitmapFormat: (NSBitmapFormat) 0 + bytesPerRow: lineStride + bitsPerPixel: pixelStride * 8]; + + unsigned char* newData = [rep bitmapData]; + memcpy (newData, pixels, lineStride * image.getHeight()); + image.releasePixelDataReadOnly (pixels); + + NSImage* im = [[NSImage alloc] init]; + [im addRepresentation: rep]; + [rep release]; + + return im; +} + +void* juce_createMouseCursorFromImage (const Image& image, int hotspotX, int hotspotY) throw() +{ + NSImage* im = juceImageToNSImage (image); + NSCursor* c = [[NSCursor alloc] initWithImage: im + hotSpot: NSMakePoint (hotspotX, hotspotY)]; + [im release]; + + return (void*) c; +} + +static void* juce_cursorFromData (const unsigned char* data, const int size, float hx, float hy) throw() +{ + Image* const im = ImageFileFormat::loadFrom ((const char*) data, size); + jassert (im != 0); + + if (im == 0) + return 0; + + void* const curs = juce_createMouseCursorFromImage (*im, + (int) (hx * im->getWidth()), + (int) (hy * im->getHeight())); + delete im; + return curs; +} + +static void* juce_cursorFromWebKitFile (const char* filename, float hx, float hy) +{ + File f ("/System/Library/Frameworks/WebKit.framework/Frameworks/WebCore.framework/Resources"); + + MemoryBlock mb; + if (f.getChildFile (filename).loadFileAsData (mb)) + return juce_cursorFromData ((const unsigned char*) mb.getData(), mb.getSize(), hx, hy); + + return 0; +} + +void* juce_createStandardMouseCursor (MouseCursor::StandardCursorType type) throw() +{ + const ScopedAutoReleasePool pool; + NSCursor* c = 0; + + switch (type) + { + case MouseCursor::NormalCursor: + c = [NSCursor arrowCursor]; + break; + + case MouseCursor::NoCursor: + { + Image blank (Image::ARGB, 8, 8, true); + return juce_createMouseCursorFromImage (blank, 0, 0); + } + + case MouseCursor::DraggingHandCursor: + c = [NSCursor openHandCursor]; + break; + + case MouseCursor::CopyingCursor: + return juce_cursorFromWebKitFile ("copyCursor.png", 0, 0); + + case MouseCursor::WaitCursor: + c = [NSCursor arrowCursor]; // avoid this on the mac, let the OS provide the beachball + break; + //return juce_cursorFromWebKitFile ("waitCursor.png", 0.5f, 0.5f); + + case MouseCursor::IBeamCursor: + c = [NSCursor IBeamCursor]; + break; + + case MouseCursor::PointingHandCursor: + c = [NSCursor pointingHandCursor]; + break; + + case MouseCursor::LeftRightResizeCursor: + c = [NSCursor resizeLeftRightCursor]; + break; + + case MouseCursor::LeftEdgeResizeCursor: + c = [NSCursor resizeLeftCursor]; + break; + + case MouseCursor::RightEdgeResizeCursor: + c = [NSCursor resizeRightCursor]; + break; + + case MouseCursor::UpDownResizeCursor: + case MouseCursor::TopEdgeResizeCursor: + case MouseCursor::BottomEdgeResizeCursor: + return juce_cursorFromWebKitFile ("northSouthResizeCursor.png", 0.5f, 0.5f); + + case MouseCursor::TopLeftCornerResizeCursor: + case MouseCursor::BottomRightCornerResizeCursor: + return juce_cursorFromWebKitFile ("northWestSouthEastResizeCursor.png", 0.5f, 0.5f); + + case MouseCursor::TopRightCornerResizeCursor: + case MouseCursor::BottomLeftCornerResizeCursor: + return juce_cursorFromWebKitFile ("northEastSouthWestResizeCursor.png", 0.5f, 0.5f); + + case MouseCursor::UpDownLeftRightResizeCursor: + return juce_cursorFromWebKitFile ("moveCursor.png", 0.5f, 0.5f); + + case MouseCursor::CrosshairCursor: + c = [NSCursor crosshairCursor]; + break; + } + + [c retain]; + return (void*) c; +} + +void juce_deleteMouseCursor (void* const cursorHandle, const bool isStandard) throw() +{ + NSCursor* c = (NSCursor*) cursorHandle; + [c release]; +} + +void MouseCursor::showInAllWindows() const throw() +{ + showInWindow (0); +} + +void MouseCursor::showInWindow (ComponentPeer*) const throw() +{ + NSCursor* const c = (NSCursor*) getHandle(); + [c set]; +} + +#endif +/********* End of inlined file: juce_mac_MouseCursor.mm *********/ + +/********* Start of inlined file: juce_mac_NSViewComponent.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +class NSViewComponentInternal : public ComponentMovementWatcher +{ + Component* const owner; + NSViewComponentPeer* currentPeer; + bool wasShowing; + +public: + NSView* const view; + + NSViewComponentInternal (NSView* const view_, Component* const owner_) + : ComponentMovementWatcher (owner_), + owner (owner_), + currentPeer (0), + wasShowing (false), + view (view_) + { + [view_ retain]; + + if (owner_->isShowing()) + componentPeerChanged(); + } + + ~NSViewComponentInternal() + { + [view removeFromSuperview]; + [view release]; + } + + void componentMovedOrResized (Component& comp, bool wasMoved, bool wasResized) + { + ComponentMovementWatcher::componentMovedOrResized (comp, wasMoved, wasResized); + + // The ComponentMovementWatcher version of this method avoids calling + // us when the top-level comp is resized, but for an NSView we need to know this + // because with inverted co-ords, we need to update the position even if the + // top-left pos hasn't changed + if (comp.isOnDesktop() && wasResized) + componentMovedOrResized (wasMoved, wasResized); + } + + void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + { + Component* const topComp = owner->getTopLevelComponent(); + + if (topComp->getPeer() != 0) + { + int x = 0, y = 0; + owner->relativePositionToOtherComponent (topComp, x, y); + + NSRect r; + r.origin.x = (float) x; + r.origin.y = (float) y; + r.size.width = (float) owner->getWidth(); + r.size.height = (float) owner->getHeight(); + r.origin.y = [[view superview] frame].size.height - (r.origin.y + r.size.height); + + [view setFrame: r]; + } + } + + void componentPeerChanged() + { + NSViewComponentPeer* const peer = dynamic_cast (owner->getPeer()); + + if (currentPeer != peer) + { + [view removeFromSuperview]; + currentPeer = peer; + + if (peer != 0) + { + [peer->view addSubview: view]; + componentMovedOrResized (false, false); + } + } + + [view setHidden: ! owner->isShowing()]; + } + + void componentVisibilityChanged (Component&) + { + componentPeerChanged(); + } + + juce_UseDebuggingNewOperator + +private: + NSViewComponentInternal (const NSViewComponentInternal&); + const NSViewComponentInternal& operator= (const NSViewComponentInternal&); +}; + +NSViewComponent::NSViewComponent() + : info (0) +{ +} + +NSViewComponent::~NSViewComponent() +{ + delete info; +} + +void NSViewComponent::setView (void* view) +{ + if (view != getView()) + { + deleteAndZero (info); + + if (view != 0) + info = new NSViewComponentInternal ((NSView*) view, this); + } +} + +void* NSViewComponent::getView() const +{ + return info == 0 ? 0 : info->view; +} + +void NSViewComponent::paint (Graphics& g) +{ +} + +#endif +/********* End of inlined file: juce_mac_NSViewComponent.mm *********/ + +/********* Start of inlined file: juce_mac_AppleRemote.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +AppleRemoteDevice::AppleRemoteDevice() + : device (0), + queue (0), + remoteId (0) +{ +} + +AppleRemoteDevice::~AppleRemoteDevice() +{ + stop(); +} + +static io_object_t getAppleRemoteDevice() throw() +{ + CFMutableDictionaryRef dict = IOServiceMatching ("AppleIRController"); + + io_iterator_t iter = 0; + io_object_t iod = 0; + + if (IOServiceGetMatchingServices (kIOMasterPortDefault, dict, &iter) == kIOReturnSuccess + && iter != 0) + { + iod = IOIteratorNext (iter); + } + + IOObjectRelease (iter); + return iod; +} + +static bool createAppleRemoteInterface (io_object_t iod, void** device) throw() +{ + jassert (*device == 0); + io_name_t classname; + + if (IOObjectGetClass (iod, classname) == kIOReturnSuccess) + { + IOCFPlugInInterface** cfPlugInInterface = 0; + SInt32 score = 0; + + if (IOCreatePlugInInterfaceForService (iod, + kIOHIDDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &cfPlugInInterface, + &score) == kIOReturnSuccess) + { + HRESULT hr = (*cfPlugInInterface)->QueryInterface (cfPlugInInterface, + CFUUIDGetUUIDBytes (kIOHIDDeviceInterfaceID), + device); + + (void) hr; + + (*cfPlugInInterface)->Release (cfPlugInInterface); + } + } + + return *device != 0; +} + +bool AppleRemoteDevice::start (const bool inExclusiveMode) throw() +{ + if (queue != 0) + return true; + + stop(); + + bool result = false; + io_object_t iod = getAppleRemoteDevice(); + + if (iod != 0) + { + if (createAppleRemoteInterface (iod, &device) && open (inExclusiveMode)) + result = true; + else + stop(); + + IOObjectRelease (iod); + } + + return result; +} + +void AppleRemoteDevice::stop() throw() +{ + if (queue != 0) + { + (*(IOHIDQueueInterface**) queue)->stop ((IOHIDQueueInterface**) queue); + (*(IOHIDQueueInterface**) queue)->dispose ((IOHIDQueueInterface**) queue); + (*(IOHIDQueueInterface**) queue)->Release ((IOHIDQueueInterface**) queue); + queue = 0; + } + + if (device != 0) + { + (*(IOHIDDeviceInterface**) device)->close ((IOHIDDeviceInterface**) device); + (*(IOHIDDeviceInterface**) device)->Release ((IOHIDDeviceInterface**) device); + device = 0; + } +} + +bool AppleRemoteDevice::isActive() const throw() +{ + return queue != 0; +} + +static void appleRemoteQueueCallback (void* const target, const IOReturn result, void*, void*) +{ + if (result == kIOReturnSuccess) + ((AppleRemoteDevice*) target)->handleCallbackInternal(); +} + +bool AppleRemoteDevice::open (const bool openInExclusiveMode) throw() +{ + Array cookies; + + CFArrayRef elements; + IOHIDDeviceInterface122** const device122 = (IOHIDDeviceInterface122**) device; + + if ((*device122)->copyMatchingElements (device122, 0, &elements) != kIOReturnSuccess) + return false; + + for (int i = 0; i < CFArrayGetCount (elements); ++i) + { + CFDictionaryRef element = (CFDictionaryRef) CFArrayGetValueAtIndex (elements, i); + + // get the cookie + CFTypeRef object = CFDictionaryGetValue (element, CFSTR (kIOHIDElementCookieKey)); + + if (object == 0 || CFGetTypeID (object) != CFNumberGetTypeID()) + continue; + + long number; + if (! CFNumberGetValue ((CFNumberRef) object, kCFNumberLongType, &number)) + continue; + + cookies.add ((int) number); + } + + CFRelease (elements); + + if ((*(IOHIDDeviceInterface**) device) + ->open ((IOHIDDeviceInterface**) device, + openInExclusiveMode ? kIOHIDOptionsTypeSeizeDevice + : kIOHIDOptionsTypeNone) == KERN_SUCCESS) + { + queue = (*(IOHIDDeviceInterface**) device)->allocQueue ((IOHIDDeviceInterface**) device); + + if (queue != 0) + { + (*(IOHIDQueueInterface**) queue)->create ((IOHIDQueueInterface**) queue, 0, 12); + + for (int i = 0; i < cookies.size(); ++i) + { + IOHIDElementCookie cookie = (IOHIDElementCookie) cookies.getUnchecked(i); + (*(IOHIDQueueInterface**) queue)->addElement ((IOHIDQueueInterface**) queue, cookie, 0); + } + + CFRunLoopSourceRef eventSource; + + if ((*(IOHIDQueueInterface**) queue) + ->createAsyncEventSource ((IOHIDQueueInterface**) queue, &eventSource) == KERN_SUCCESS) + { + if ((*(IOHIDQueueInterface**) queue)->setEventCallout ((IOHIDQueueInterface**) queue, + appleRemoteQueueCallback, this, 0) == KERN_SUCCESS) + { + CFRunLoopAddSource (CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode); + + (*(IOHIDQueueInterface**) queue)->start ((IOHIDQueueInterface**) queue); + + return true; + } + } + } + } + + return false; +} + +void AppleRemoteDevice::handleCallbackInternal() +{ + int totalValues = 0; + AbsoluteTime nullTime = { 0, 0 }; + char cookies [12]; + int numCookies = 0; + + while (numCookies < numElementsInArray (cookies)) + { + IOHIDEventStruct e; + + if ((*(IOHIDQueueInterface**) queue)->getNextEvent ((IOHIDQueueInterface**) queue, &e, nullTime, 0) != kIOReturnSuccess) + break; + + if ((int) e.elementCookie == 19) + { + remoteId = e.value; + buttonPressed (switched, false); + } + else + { + totalValues += e.value; + cookies [numCookies++] = (char) (pointer_sized_int) e.elementCookie; + } + } + + cookies [numCookies++] = 0; + //DBG (String::toHexString ((uint8*) cookies, numCookies, 1) + " " + String (totalValues)); + + static const char buttonPatterns[] = + { + 0x1f, 0x14, 0x12, 0x1f, 0x14, 0x12, 0, + 0x1f, 0x15, 0x12, 0x1f, 0x15, 0x12, 0, + 0x1f, 0x1d, 0x1c, 0x12, 0, + 0x1f, 0x1e, 0x1c, 0x12, 0, + 0x1f, 0x16, 0x12, 0x1f, 0x16, 0x12, 0, + 0x1f, 0x17, 0x12, 0x1f, 0x17, 0x12, 0, + 0x1f, 0x12, 0x04, 0x02, 0, + 0x1f, 0x12, 0x03, 0x02, 0, + 0x1f, 0x12, 0x1f, 0x12, 0, + 0x23, 0x1f, 0x12, 0x23, 0x1f, 0x12, 0, + 19, 0 + }; + + int buttonNum = (int) menuButton; + int i = 0; + + while (i < numElementsInArray (buttonPatterns)) + { + if (strcmp (cookies, buttonPatterns + i) == 0) + { + buttonPressed ((ButtonType) buttonNum, totalValues > 0); + break; + } + + i += strlen (buttonPatterns + i) + 1; + ++buttonNum; + } +} + +#endif +/********* End of inlined file: juce_mac_AppleRemote.mm *********/ + +/********* Start of inlined file: juce_mac_OpenGLComponent.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE && JUCE_OPENGL + +END_JUCE_NAMESPACE + +#define ThreadSafeNSOpenGLView MakeObjCClassName(ThreadSafeNSOpenGLView) + +@interface ThreadSafeNSOpenGLView : NSOpenGLView +{ + CriticalSection* contextLock; + bool needsUpdate; +} + +- (id) initWithFrame: (NSRect) frameRect pixelFormat: (NSOpenGLPixelFormat*) format; +- (bool) makeActive; +- (void) makeInactive; +- (void) reshape; +@end + +@implementation ThreadSafeNSOpenGLView + +- (id) initWithFrame: (NSRect) frameRect + pixelFormat: (NSOpenGLPixelFormat*) format +{ + contextLock = new CriticalSection(); + self = [super initWithFrame: frameRect pixelFormat: format]; + + if (self != nil) + [[NSNotificationCenter defaultCenter] addObserver: self + selector: @selector (_surfaceNeedsUpdate:) + name: NSViewGlobalFrameDidChangeNotification + object: self]; + return self; +} + +- (void) dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver: self]; + delete contextLock; + [super dealloc]; +} + +- (bool) makeActive +{ + const ScopedLock sl (*contextLock); + + if ([self openGLContext] == 0) + return false; + + [[self openGLContext] makeCurrentContext]; + + if (needsUpdate) + { + [super update]; + needsUpdate = false; + } + + return true; +} + +- (void) makeInactive +{ + const ScopedLock sl (*contextLock); + [NSOpenGLContext clearCurrentContext]; +} + +- (void) _surfaceNeedsUpdate: (NSNotification*) notification +{ + const ScopedLock sl (*contextLock); + needsUpdate = true; +} + +- (void) update +{ + const ScopedLock sl (*contextLock); + needsUpdate = true; +} + +- (void) reshape +{ + const ScopedLock sl (*contextLock); + needsUpdate = true; +} + +@end +BEGIN_JUCE_NAMESPACE + +class WindowedGLContext : public OpenGLContext +{ +public: + WindowedGLContext (Component* const component, + const OpenGLPixelFormat& pixelFormat_, + NSOpenGLContext* sharedContext) + : renderContext (0), + pixelFormat (pixelFormat_) + { + jassert (component != 0); + + NSOpenGLPixelFormatAttribute attribs [64]; + int n = 0; + attribs[n++] = NSOpenGLPFADoubleBuffer; + attribs[n++] = NSOpenGLPFAAccelerated; + attribs[n++] = NSOpenGLPFAMPSafe; // NSOpenGLPFAAccelerated, NSOpenGLPFAMultiScreen, NSOpenGLPFASingleRenderer + attribs[n++] = NSOpenGLPFAColorSize; + attribs[n++] = (NSOpenGLPixelFormatAttribute) jmax (pixelFormat.redBits, + pixelFormat.greenBits, + pixelFormat.blueBits); + attribs[n++] = NSOpenGLPFAAlphaSize; + attribs[n++] = (NSOpenGLPixelFormatAttribute) pixelFormat.alphaBits; + attribs[n++] = NSOpenGLPFADepthSize; + attribs[n++] = (NSOpenGLPixelFormatAttribute) pixelFormat.depthBufferBits; + attribs[n++] = NSOpenGLPFAStencilSize; + attribs[n++] = (NSOpenGLPixelFormatAttribute) pixelFormat.stencilBufferBits; + attribs[n++] = NSOpenGLPFAAccumSize; + attribs[n++] = (NSOpenGLPixelFormatAttribute) jmax (pixelFormat.accumulationBufferRedBits, + pixelFormat.accumulationBufferGreenBits, + pixelFormat.accumulationBufferBlueBits, + pixelFormat.accumulationBufferAlphaBits); + + // xxx not sure how to do fullSceneAntiAliasingNumSamples.. + attribs[n++] = NSOpenGLPFASampleBuffers; + attribs[n++] = (NSOpenGLPixelFormatAttribute) 1; + attribs[n++] = NSOpenGLPFAClosestPolicy; + attribs[n++] = NSOpenGLPFANoRecovery; + attribs[n++] = (NSOpenGLPixelFormatAttribute) 0; + + NSOpenGLPixelFormat* format + = [[NSOpenGLPixelFormat alloc] initWithAttributes: attribs]; + + view = [[ThreadSafeNSOpenGLView alloc] initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f) + pixelFormat: format]; + + renderContext = [[[NSOpenGLContext alloc] initWithFormat: format + shareContext: sharedContext] autorelease]; + + const GLint swapInterval = 1; + [renderContext setValues: &swapInterval forParameter: NSOpenGLCPSwapInterval]; + + [view setOpenGLContext: renderContext]; + [renderContext setView: view]; + + [format release]; + + viewHolder = new NSViewComponentInternal (view, component); + } + + ~WindowedGLContext() + { + makeInactive(); + [renderContext setView: nil]; + delete viewHolder; + } + + bool makeActive() const throw() + { + jassert (renderContext != 0); + [view makeActive]; + return isActive(); + } + + bool makeInactive() const throw() + { + [view makeInactive]; + return true; + } + + bool isActive() const throw() + { + return [NSOpenGLContext currentContext] == renderContext; + } + + const OpenGLPixelFormat getPixelFormat() const { return pixelFormat; } + void* getRawContext() const throw() { return renderContext; } + + void updateWindowPosition (int x, int y, int w, int h, int outerWindowHeight) + { + } + + void swapBuffers() + { + [renderContext flushBuffer]; + } + + bool setSwapInterval (const int numFramesPerSwap) + { + [renderContext setValues: (const GLint*) &numFramesPerSwap + forParameter: NSOpenGLCPSwapInterval]; + return true; + } + + int getSwapInterval() const + { + GLint numFrames = 0; + [renderContext getValues: &numFrames + forParameter: NSOpenGLCPSwapInterval]; + return numFrames; + } + + void repaint() + { + // we need to invalidate the juce view that holds this gl view, to make it + // cause a repaint callback + NSView* v = (NSView*) viewHolder->view; + NSRect r = [v frame]; + + // bit of a bodge here.. if we only invalidate the area of the gl component, + // it's completely covered by the NSOpenGLView, so the OS throws away the + // repaint message, thus never causing our paint() callback, and never repainting + // the comp. So invalidating just a little bit around the edge helps.. + [[v superview] setNeedsDisplayInRect: NSInsetRect (r, -2.0f, -2.0f)]; + } + + void* getNativeWindowHandle() const { return viewHolder->view; } + + juce_UseDebuggingNewOperator + + NSOpenGLContext* renderContext; + ThreadSafeNSOpenGLView* view; + +private: + OpenGLPixelFormat pixelFormat; + NSViewComponentInternal* viewHolder; + + WindowedGLContext (const WindowedGLContext&); + const WindowedGLContext& operator= (const WindowedGLContext&); +}; + +OpenGLContext* OpenGLContext::createContextForWindow (Component* const component, + const OpenGLPixelFormat& pixelFormat, + const OpenGLContext* const contextToShareWith) +{ + WindowedGLContext* c = new WindowedGLContext (component, pixelFormat, + contextToShareWith != 0 ? (NSOpenGLContext*) contextToShareWith->getRawContext() : 0); + + if (c->renderContext == 0) + deleteAndZero (c); + + return c; +} + +void* OpenGLComponent::getNativeWindowHandle() const +{ + return context != 0 ? ((WindowedGLContext*) context)->getNativeWindowHandle() + : 0; +} + +void juce_glViewport (const int w, const int h) +{ + glViewport (0, 0, w, h); +} + +void OpenGLPixelFormat::getAvailablePixelFormats (Component* /*component*/, + OwnedArray & results) +{ +/* GLint attribs [64]; + int n = 0; + attribs[n++] = AGL_RGBA; + attribs[n++] = AGL_DOUBLEBUFFER; + attribs[n++] = AGL_ACCELERATED; + attribs[n++] = AGL_NO_RECOVERY; + attribs[n++] = AGL_NONE; + + AGLPixelFormat p = aglChoosePixelFormat (0, 0, attribs); + + while (p != 0) + { + OpenGLPixelFormat* const pf = new OpenGLPixelFormat(); + pf->redBits = getAGLAttribute (p, AGL_RED_SIZE); + pf->greenBits = getAGLAttribute (p, AGL_GREEN_SIZE); + pf->blueBits = getAGLAttribute (p, AGL_BLUE_SIZE); + pf->alphaBits = getAGLAttribute (p, AGL_ALPHA_SIZE); + pf->depthBufferBits = getAGLAttribute (p, AGL_DEPTH_SIZE); + pf->stencilBufferBits = getAGLAttribute (p, AGL_STENCIL_SIZE); + pf->accumulationBufferRedBits = getAGLAttribute (p, AGL_ACCUM_RED_SIZE); + pf->accumulationBufferGreenBits = getAGLAttribute (p, AGL_ACCUM_GREEN_SIZE); + pf->accumulationBufferBlueBits = getAGLAttribute (p, AGL_ACCUM_BLUE_SIZE); + pf->accumulationBufferAlphaBits = getAGLAttribute (p, AGL_ACCUM_ALPHA_SIZE); + + results.add (pf); + + p = aglNextPixelFormat (p); + }*/ + + //jassertfalse //xxx can't see how you do this in cocoa! +} + +#endif +/********* End of inlined file: juce_mac_OpenGLComponent.mm *********/ + +/********* Start of inlined file: juce_mac_MainMenu.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +class JuceMainMenuHandler; + +END_JUCE_NAMESPACE +using namespace JUCE_NAMESPACE; + +#define JuceMenuCallback MakeObjCClassName(JuceMenuCallback) + +#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +@interface JuceMenuCallback : NSObject +#else +@interface JuceMenuCallback : NSObject +#endif +{ + JuceMainMenuHandler* owner; +} + +- (JuceMenuCallback*) initWithOwner: (JuceMainMenuHandler*) owner_; +- (void) dealloc; +- (void) menuItemInvoked: (id) menu; +- (void) menuNeedsUpdate: (NSMenu*) menu; +@end +BEGIN_JUCE_NAMESPACE + +class JuceMainMenuHandler : private MenuBarModelListener, + private DeletedAtShutdown +{ +public: + static JuceMainMenuHandler* instance; + + JuceMainMenuHandler() throw() + : currentModel (0), + lastUpdateTime (0) + { + callback = [[JuceMenuCallback alloc] initWithOwner: this]; + } + + ~JuceMainMenuHandler() throw() + { + setMenu (0); + + jassert (instance == this); + instance = 0; + + [callback release]; + } + + void setMenu (MenuBarModel* const newMenuBarModel) throw() + { + if (currentModel != newMenuBarModel) + { + if (currentModel != 0) + currentModel->removeListener (this); + + currentModel = newMenuBarModel; + + if (currentModel != 0) + currentModel->addListener (this); + + menuBarItemsChanged (0); + } + } + + void addSubMenu (NSMenu* parent, const PopupMenu& child, + const String& name, const int menuId, const int tag) + { + NSMenuItem* item = [parent addItemWithTitle: juceStringToNS (name) + action: nil + keyEquivalent: @""]; + [item setTag: tag]; + + NSMenu* sub = createMenu (child, name, menuId, tag); + + [parent setSubmenu: sub forItem: item]; + [sub setAutoenablesItems: false]; + [sub release]; + } + + void updateSubMenu (NSMenuItem* parentItem, const PopupMenu& menuToCopy, + const String& name, const int menuId, const int tag) + { + [parentItem setTag: tag]; + NSMenu* menu = [parentItem submenu]; + + [menu setTitle: juceStringToNS (name)]; + + while ([menu numberOfItems] > 0) + [menu removeItemAtIndex: 0]; + + PopupMenu::MenuItemIterator iter (menuToCopy); + + while (iter.next()) + addMenuItem (iter, menu, menuId, tag); + + [menu setAutoenablesItems: false]; + [menu update]; + } + + void menuBarItemsChanged (MenuBarModel*) + { + lastUpdateTime = Time::getMillisecondCounter(); + + StringArray menuNames; + if (currentModel != 0) + menuNames = currentModel->getMenuBarNames(); + + NSMenu* menuBar = [NSApp mainMenu]; + while ([menuBar numberOfItems] > 1 + menuNames.size()) + [menuBar removeItemAtIndex: [menuBar numberOfItems] - 1]; + + int menuId = 1; + + for (int i = 0; i < menuNames.size(); ++i) + { + const PopupMenu menu (currentModel->getMenuForIndex (i, menuNames [i])); + + if (i >= [menuBar numberOfItems] - 1) + addSubMenu (menuBar, menu, menuNames[i], menuId, i); + else + updateSubMenu ([menuBar itemAtIndex: 1 + i], menu, menuNames[i], menuId, i); + } + } + + static void flashMenuBar (NSMenu* menu) + { + const unichar f35Key = NSF35FunctionKey; + NSString* f35String = [NSString stringWithCharacters: &f35Key length: 1]; + + NSMenuItem* item = [[NSMenuItem alloc] initWithTitle: @"x" + action: nil + keyEquivalent: f35String]; + [item setTarget: nil]; + [menu insertItem: item atIndex: [menu numberOfItems]]; + [item release]; + + NSEvent* f35Event = [NSEvent keyEventWithType: NSKeyDown + location: NSZeroPoint + modifierFlags: NSCommandKeyMask + timestamp: 0 + windowNumber: 0 + context: [NSGraphicsContext currentContext] + characters: f35String + charactersIgnoringModifiers: f35String + isARepeat: NO + keyCode: 0]; + + [menu performKeyEquivalent: f35Event]; + [menu removeItem: item]; + } + + static NSMenuItem* findMenuItem (NSMenu* const menu, const ApplicationCommandTarget::InvocationInfo& info) + { + for (int i = [menu numberOfItems]; --i >= 0;) + { + NSMenuItem* m = [menu itemAtIndex: i]; + if ([m tag] == info.commandID) + return m; + + if ([m submenu] != 0) + { + NSMenuItem* found = findMenuItem ([m submenu], info); + if (found != 0) + return found; + } + } + + return 0; + } + + void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo& info) + { + NSMenuItem* item = findMenuItem ([NSApp mainMenu], info); + + if (item != 0) + flashMenuBar ([item menu]); + } + + void updateMenus() + { + if (Time::getMillisecondCounter() > lastUpdateTime + 500) + menuBarItemsChanged (0); + } + + void invoke (const int commandId, ApplicationCommandManager* const commandManager, const int topLevelIndex) const + { + if (currentModel != 0) + { + if (commandManager != 0) + { + ApplicationCommandTarget::InvocationInfo info (commandId); + info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromMenu; + + commandManager->invoke (info, true); + } + + currentModel->menuItemSelected (commandId, topLevelIndex); + } + } + + MenuBarModel* currentModel; + uint32 lastUpdateTime; + + void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo, + const int topLevelMenuId, const int topLevelIndex) + { + NSString* text = juceStringToNS (iter.itemName.upToFirstOccurrenceOf (T(""), false, true)); + + if (text == 0) + text = @""; + + if (iter.isSeparator) + { + [menuToAddTo addItem: [NSMenuItem separatorItem]]; + } + else if (iter.isSectionHeader) + { + NSMenuItem* item = [menuToAddTo addItemWithTitle: text + action: nil + keyEquivalent: @""]; + + [item setEnabled: false]; + } + else if (iter.subMenu != 0) + { + NSMenuItem* item = [menuToAddTo addItemWithTitle: text + action: nil + keyEquivalent: @""]; + + [item setTag: iter.itemId]; + [item setEnabled: iter.isEnabled]; + + NSMenu* sub = createMenu (*iter.subMenu, iter.itemName, topLevelMenuId, topLevelIndex); + [sub setDelegate: nil]; + [menuToAddTo setSubmenu: sub forItem: item]; + } + else + { + NSMenuItem* item = [menuToAddTo addItemWithTitle: text + action: @selector (menuItemInvoked:) + keyEquivalent: @""]; + + [item setTag: iter.itemId]; + [item setEnabled: iter.isEnabled]; + [item setState: iter.isTicked ? NSOnState : NSOffState]; + [item setTarget: (id) callback]; + + NSMutableArray* info = [NSMutableArray arrayWithObject: [NSNumber numberWithUnsignedLongLong: (pointer_sized_int) (void*) iter.commandManager]]; + [info addObject: [NSNumber numberWithInt: topLevelIndex]]; + [item setRepresentedObject: info]; + + if (iter.commandManager != 0) + { + const Array keyPresses (iter.commandManager->getKeyMappings() + ->getKeyPressesAssignedToCommand (iter.itemId)); + + if (keyPresses.size() > 0) + { + const KeyPress& kp = keyPresses.getReference(0); + + juce_wchar key = kp.getTextCharacter(); + + if (kp.getKeyCode() == KeyPress::backspaceKey) + key = NSBackspaceCharacter; + else if (kp.getKeyCode() == KeyPress::deleteKey) + key = NSDeleteCharacter; + else if (key == 0) + key = (juce_wchar) kp.getKeyCode(); + + unsigned int mods = 0; + if (kp.getModifiers().isShiftDown()) + mods |= NSShiftKeyMask; + if (kp.getModifiers().isCtrlDown()) + mods |= NSControlKeyMask; + if (kp.getModifiers().isAltDown()) + mods |= NSAlternateKeyMask; + if (kp.getModifiers().isCommandDown()) + mods |= NSCommandKeyMask; + + [item setKeyEquivalent: juceStringToNS (String::charToString (key))]; + [item setKeyEquivalentModifierMask: mods]; + } + } + } + } + + JuceMenuCallback* callback; +private: + + NSMenu* createMenu (const PopupMenu menu, + const String& menuName, + const int topLevelMenuId, + const int topLevelIndex) + { + NSMenu* m = [[NSMenu alloc] initWithTitle: juceStringToNS (menuName)]; + + [m setAutoenablesItems: false]; + [m setDelegate: callback]; + + PopupMenu::MenuItemIterator iter (menu); + + while (iter.next()) + addMenuItem (iter, m, topLevelMenuId, topLevelIndex); + + [m update]; + return m; + } +}; + +JuceMainMenuHandler* JuceMainMenuHandler::instance = 0; + +END_JUCE_NAMESPACE +@implementation JuceMenuCallback + +- (JuceMenuCallback*) initWithOwner: (JuceMainMenuHandler*) owner_ +{ + [super init]; + owner = owner_; + return self; +} + +- (void) dealloc +{ + [super dealloc]; +} + +- (void) menuItemInvoked: (id) menu +{ + NSMenuItem* item = (NSMenuItem*) menu; + + if ([[item representedObject] isKindOfClass: [NSArray class]]) + { + NSArray* info = (NSArray*) [item representedObject]; + + owner->invoke ([item tag], + (ApplicationCommandManager*) (pointer_sized_int) + [((NSNumber*) [info objectAtIndex: 0]) unsignedLongLongValue], + (int) [((NSNumber*) [info objectAtIndex: 1]) intValue]); + } +} + +- (void) menuNeedsUpdate: (NSMenu*) menu; +{ + if (JuceMainMenuHandler::instance != 0) + JuceMainMenuHandler::instance->updateMenus(); +} + +@end +BEGIN_JUCE_NAMESPACE + +static NSMenu* createStandardAppMenu (NSMenu* menu, const String& appName, + const PopupMenu* extraItems) +{ + if (extraItems != 0 && JuceMainMenuHandler::instance != 0 && extraItems->getNumItems() > 0) + { + PopupMenu::MenuItemIterator iter (*extraItems); + + while (iter.next()) + JuceMainMenuHandler::instance->addMenuItem (iter, menu, 0, -1); + + [menu addItem: [NSMenuItem separatorItem]]; + } + + NSMenuItem* item; + + // Services... + item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (@"Services", nil) + action: nil keyEquivalent: @""]; + [menu addItem: item]; + [item release]; + NSMenu* servicesMenu = [[NSMenu alloc] initWithTitle: @"Services"]; + [menu setSubmenu: servicesMenu forItem: item]; + [NSApp setServicesMenu: servicesMenu]; + [servicesMenu release]; + [menu addItem: [NSMenuItem separatorItem]]; + + // Hide + Show stuff... + item = [[NSMenuItem alloc] initWithTitle: juceStringToNS ("Hide " + appName) + action: @selector (hide:) keyEquivalent: @"h"]; + [item setTarget: NSApp]; + [menu addItem: item]; + [item release]; + + item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (@"Hide Others", nil) + action: @selector (hideOtherApplications:) keyEquivalent: @"h"]; + [item setKeyEquivalentModifierMask: NSCommandKeyMask | NSAlternateKeyMask]; + [item setTarget: NSApp]; + [menu addItem: item]; + [item release]; + + item = [[NSMenuItem alloc] initWithTitle: NSLocalizedString (@"Show All", nil) + action: @selector (unhideAllApplications:) keyEquivalent: @""]; + [item setTarget: NSApp]; + [menu addItem: item]; + [item release]; + + [menu addItem: [NSMenuItem separatorItem]]; + + // Quit item.... + item = [[NSMenuItem alloc] initWithTitle: juceStringToNS ("Quit " + appName) + action: @selector (terminate:) keyEquivalent: @"q"]; + + [item setTarget: NSApp]; + [menu addItem: item]; + [item release]; + + return menu; +} + +// Since our app has no NIB, this initialises a standard app menu... +static void rebuildMainMenu (const PopupMenu* extraItems) +{ + // this can't be used in a plugin! + jassert (JUCEApplication::getInstance() != 0); + + if (JUCEApplication::getInstance() != 0) + { + const ScopedAutoReleasePool pool; + + NSMenu* mainMenu = [[NSMenu alloc] initWithTitle: @"MainMenu"]; + NSMenuItem* item = [mainMenu addItemWithTitle: @"Apple" action: nil keyEquivalent: @""]; + + NSMenu* appMenu = [[NSMenu alloc] initWithTitle: @"Apple"]; + + [NSApp performSelector: @selector (setAppleMenu:) withObject: appMenu]; + [mainMenu setSubmenu: appMenu forItem: item]; + + [NSApp setMainMenu: mainMenu]; + createStandardAppMenu (appMenu, JUCEApplication::getInstance()->getApplicationName(), extraItems); + + [appMenu release]; + [mainMenu release]; + } +} + +void MenuBarModel::setMacMainMenu (MenuBarModel* newMenuBarModel, + const PopupMenu* extraAppleMenuItems) throw() +{ + if (getMacMainMenu() != newMenuBarModel) + { + const ScopedAutoReleasePool pool; + + if (newMenuBarModel == 0) + { + delete JuceMainMenuHandler::instance; + jassert (JuceMainMenuHandler::instance == 0); // should be zeroed in the destructor + jassert (extraAppleMenuItems == 0); // you can't specify some extra items without also supplying a model + + extraAppleMenuItems = 0; + } + else + { + if (JuceMainMenuHandler::instance == 0) + JuceMainMenuHandler::instance = new JuceMainMenuHandler(); + + JuceMainMenuHandler::instance->setMenu (newMenuBarModel); + } + } + + rebuildMainMenu (extraAppleMenuItems); + + if (newMenuBarModel != 0) + newMenuBarModel->menuItemsChanged(); +} + +MenuBarModel* MenuBarModel::getMacMainMenu() throw() +{ + return JuceMainMenuHandler::instance != 0 + ? JuceMainMenuHandler::instance->currentModel : 0; +} + +void initialiseMainMenu() +{ + if (JUCEApplication::getInstance() != 0) // only needed in an app + rebuildMainMenu (0); +} + +#endif +/********* End of inlined file: juce_mac_MainMenu.mm *********/ + +/********* Start of inlined file: juce_mac_FileChooser.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +END_JUCE_NAMESPACE +using namespace JUCE_NAMESPACE; + +#define JuceFileChooserDelegate MakeObjCClassName(JuceFileChooserDelegate) + +#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +@interface JuceFileChooserDelegate : NSObject +#else +@interface JuceFileChooserDelegate : NSObject +#endif +{ + StringArray* filters; +} + +- (JuceFileChooserDelegate*) initWithFilters: (StringArray*) filters_; +- (void) dealloc; +- (BOOL) panel: (id) sender shouldShowFilename: (NSString*) filename; + +@end + +@implementation JuceFileChooserDelegate +- (JuceFileChooserDelegate*) initWithFilters: (StringArray*) filters_ +{ + [super init]; + filters = filters_; + return self; +} + +- (void) dealloc +{ + delete filters; + [super dealloc]; +} + +- (BOOL) panel: (id) sender shouldShowFilename: (NSString*) filename +{ + const String fname (nsStringToJuce (filename)); + + for (int i = filters->size(); --i >= 0;) + if (fname.matchesWildcard ((*filters)[i], true)) + return true; + + return File (fname).isDirectory(); +} +@end + +BEGIN_JUCE_NAMESPACE + +void FileChooser::showPlatformDialog (OwnedArray& results, + const String& title, + const File& currentFileOrDirectory, + const String& filter, + bool selectsDirectory, + bool isSaveDialogue, + bool warnAboutOverwritingExistingFiles, + bool selectMultipleFiles, + FilePreviewComponent* extraInfoComponent) +{ + const ScopedAutoReleasePool pool; + + StringArray* filters = new StringArray(); + filters->addTokens (filter.replaceCharacters (T(",:"), T(";;")), T(";"), 0); + filters->trim(); + filters->removeEmptyStrings(); + + JuceFileChooserDelegate* delegate = [[JuceFileChooserDelegate alloc] initWithFilters: filters]; + [delegate autorelease]; + + NSSavePanel* panel = isSaveDialogue ? [NSSavePanel savePanel] + : [NSOpenPanel openPanel]; + + [panel setTitle: juceStringToNS (title)]; + + if (! isSaveDialogue) + { + NSOpenPanel* openPanel = (NSOpenPanel*) panel; + [openPanel setCanChooseDirectories: selectsDirectory]; + [openPanel setCanChooseFiles: ! selectsDirectory]; + [openPanel setAllowsMultipleSelection: selectMultipleFiles]; + } + + [panel setDelegate: delegate]; + + String directory, filename; + + if (currentFileOrDirectory.isDirectory()) + { + directory = currentFileOrDirectory.getFullPathName(); + } + else + { + directory = currentFileOrDirectory.getParentDirectory().getFullPathName(); + filename = currentFileOrDirectory.getFileName(); + } + + if ([panel runModalForDirectory: juceStringToNS (directory) + file: juceStringToNS (filename)] + == NSOKButton) + { + if (isSaveDialogue) + { + results.add (new File (nsStringToJuce ([panel filename]))); + } + else + { + NSOpenPanel* openPanel = (NSOpenPanel*) panel; + NSArray* urls = [openPanel filenames]; + for (unsigned int i = 0; i < [urls count]; ++i) + { + NSString* f = [urls objectAtIndex: i]; + results.add (new File (nsStringToJuce (f))); + } + } + } + + [panel setDelegate: nil]; +} + +#endif +/********* End of inlined file: juce_mac_FileChooser.mm *********/ + +/********* Start of inlined file: juce_mac_QuickTimeMovieComponent.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE && JUCE_QUICKTIME + +#define theMovie ((QTMovie*) movie) + +QuickTimeMovieComponent::QuickTimeMovieComponent() + : movie (0) +{ + setOpaque (true); + setVisible (true); + + QTMovieView* view = [[QTMovieView alloc] initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)]; + setView (view); +} + +QuickTimeMovieComponent::~QuickTimeMovieComponent() +{ + closeMovie(); + setView (0); +} + +bool QuickTimeMovieComponent::isQuickTimeAvailable() throw() +{ + return true; +} + +static QTMovie* openMovieFromStream (InputStream* movieStream, File& movieFile) +{ + // unfortunately, QTMovie objects can only be created on the main thread.. + jassert (MessageManager::getInstance()->isThisTheMessageThread()); + + QTMovie* movie = 0; + + FileInputStream* const fin = dynamic_cast (movieStream); + + if (fin != 0) + { + movieFile = fin->getFile(); + movie = [QTMovie movieWithFile: juceStringToNS (movieFile.getFullPathName()) + error: nil]; + } + else + { + MemoryBlock temp; + movieStream->readIntoMemoryBlock (temp); + + const char* const suffixesToTry[] = { ".mov", ".mp3", ".avi", ".m4a" }; + + for (int i = 0; i < numElementsInArray (suffixesToTry); ++i) + { + movie = [QTMovie movieWithDataReference: [QTDataReference dataReferenceWithReferenceToData: [NSData dataWithBytes: temp.getData() + length: temp.getSize()] + name: [NSString stringWithUTF8String: suffixesToTry[i]] + MIMEType: @""] + error: nil]; + + if (movie != 0) + break; + } + } + + return movie; +} + +bool QuickTimeMovieComponent::loadMovie (InputStream* movieStream, + const bool controllerVisible) +{ + closeMovie(); + + if (getPeer() == 0) + { + // To open a movie, this component must be visible inside a functioning window, so that + // the QT control can be assigned to the window. + jassertfalse + return false; + } + + movie = openMovieFromStream (movieStream, movieFile); + + [theMovie retain]; + QTMovieView* view = (QTMovieView*) getView(); + [view setMovie: theMovie]; + [view setControllerVisible: controllerVisible]; + setLooping (looping); + + return movie != nil; +} + +void QuickTimeMovieComponent::closeMovie() +{ + stop(); + QTMovieView* view = (QTMovieView*) getView(); + [view setMovie: nil]; + [theMovie release]; + movie = 0; + movieFile = File::nonexistent; +} + +bool QuickTimeMovieComponent::isMovieOpen() const +{ + return movie != nil; +} + +const File QuickTimeMovieComponent::getCurrentMovieFile() const +{ + return movieFile; +} + +void QuickTimeMovieComponent::play() +{ + [theMovie play]; +} + +void QuickTimeMovieComponent::stop() +{ + [theMovie stop]; +} + +bool QuickTimeMovieComponent::isPlaying() const +{ + return movie != 0 && [theMovie rate] != 0; +} + +void QuickTimeMovieComponent::setPosition (const double seconds) +{ + if (movie != 0) + { + QTTime t; + t.timeValue = (uint64) (100000.0 * seconds); + t.timeScale = 100000; + t.flags = 0; + + [theMovie setCurrentTime: t]; + } +} + +double QuickTimeMovieComponent::getPosition() const +{ + if (movie == 0) + return 0.0; + + QTTime t = [theMovie currentTime]; + return t.timeValue / (double) t.timeScale; +} + +void QuickTimeMovieComponent::setSpeed (const float newSpeed) +{ + [theMovie setRate: newSpeed]; +} + +double QuickTimeMovieComponent::getMovieDuration() const +{ + if (movie == 0) + return 0.0; + + QTTime t = [theMovie duration]; + return t.timeValue / (double) t.timeScale; +} + +void QuickTimeMovieComponent::setLooping (const bool shouldLoop) +{ + looping = shouldLoop; + + [theMovie setAttribute: [NSNumber numberWithBool: shouldLoop] + forKey: QTMovieLoopsAttribute]; +} + +bool QuickTimeMovieComponent::isLooping() const +{ + return looping; +} + +void QuickTimeMovieComponent::setMovieVolume (const float newVolume) +{ + [theMovie setVolume: newVolume]; +} + +float QuickTimeMovieComponent::getMovieVolume() const +{ + return movie != 0 ? [theMovie volume] : 0.0f; +} + +void QuickTimeMovieComponent::getMovieNormalSize (int& width, int& height) const +{ + width = 0; + height = 0; + + if (movie != 0) + { + NSSize s = [[theMovie attributeForKey: QTMovieNaturalSizeAttribute] sizeValue]; + width = s.width; + height = s.height; + } +} + +void QuickTimeMovieComponent::paint (Graphics& g) +{ + if (movie == 0) + g.fillAll (Colours::black); +} + +bool QuickTimeMovieComponent::isControllerVisible() const +{ + return controllerVisible; +} + +bool QuickTimeMovieComponent::loadMovie (const File& movieFile_, + const bool isControllerVisible) +{ + const bool ok = loadMovie ((InputStream*) movieFile_.createInputStream(), isControllerVisible); + movieFile = movieFile_; + return ok; +} + +void QuickTimeMovieComponent::goToStart() +{ + setPosition (0.0); +} + +void QuickTimeMovieComponent::setBoundsWithCorrectAspectRatio (const Rectangle& spaceToFitWithin, + const RectanglePlacement& placement) +{ + int normalWidth, normalHeight; + getMovieNormalSize (normalWidth, normalHeight); + + if (normalWidth > 0 && normalHeight > 0 && ! spaceToFitWithin.isEmpty()) + { + double x = 0.0, y = 0.0, w = normalWidth, h = normalHeight; + + placement.applyTo (x, y, w, h, + spaceToFitWithin.getX(), spaceToFitWithin.getY(), + spaceToFitWithin.getWidth(), spaceToFitWithin.getHeight()); + + if (w > 0 && h > 0) + { + setBounds (roundDoubleToInt (x), roundDoubleToInt (y), + roundDoubleToInt (w), roundDoubleToInt (h)); + } + } + else + { + setBounds (spaceToFitWithin); + } +} + +#if ! (JUCE_MAC && JUCE_64BIT) + +bool juce_OpenQuickTimeMovieFromStream (InputStream* movieStream, Movie& result, Handle& dataHandle) +{ + if (movieStream == 0) + return false; + + File file; + QTMovie* movie = openMovieFromStream (movieStream, file); + + if (movie != nil) + result = [movie quickTimeMovie]; + + return movie != nil; +} + +#endif + +#endif +/********* End of inlined file: juce_mac_QuickTimeMovieComponent.mm *********/ + +/********* Start of inlined file: juce_mac_AudioCDBurner.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE && JUCE_USE_CDBURNER + +END_JUCE_NAMESPACE + +#define OpenDiskDevice MakeObjCClassName(OpenDiskDevice) + +@interface OpenDiskDevice : NSObject +{ + DRDevice* device; + + NSMutableArray* tracks; +} + +- (OpenDiskDevice*) initWithDevice: (DRDevice*) device; +- (void) dealloc; +- (bool) isDiskPresent; +- (int) getNumAvailableAudioBlocks; +- (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source numSamples: (int) numSamples_; +- (void) burn: (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener*) listener errorString: (JUCE_NAMESPACE::String*) error + ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting; +@end + +#define AudioTrackProducer MakeObjCClassName(AudioTrackProducer) + +@interface AudioTrackProducer : NSObject +{ + JUCE_NAMESPACE::AudioSource* source; + int readPosition, lengthInFrames; +} + +- (AudioTrackProducer*) init: (int) lengthInFrames; +- (AudioTrackProducer*) initWithAudioSource: (JUCE_NAMESPACE::AudioSource*) source numSamples: (int) lengthInSamples; +- (void) dealloc; +- (void) setupTrackProperties: (DRTrack*) track; + +- (void) cleanupTrackAfterBurn: (DRTrack*) track; +- (BOOL) cleanupTrackAfterVerification:(DRTrack*)track; +- (uint64_t) estimateLengthOfTrack:(DRTrack*)track; +- (BOOL) prepareTrack:(DRTrack*)track forBurn:(DRBurn*)burn + toMedia:(NSDictionary*)mediaInfo; +- (BOOL) prepareTrackForVerification:(DRTrack*)track; +- (uint32_t) produceDataForTrack:(DRTrack*)track intoBuffer:(char*)buffer + length:(uint32_t)bufferLength atAddress:(uint64_t)address + blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags; +- (uint32_t) producePreGapForTrack:(DRTrack*)track + intoBuffer:(char*)buffer length:(uint32_t)bufferLength + atAddress:(uint64_t)address blockSize:(uint32_t)blockSize + ioFlags:(uint32_t*)flags; +- (BOOL) verifyDataForTrack:(DRTrack*)track inBuffer:(const char*)buffer + length:(uint32_t)bufferLength atAddress:(uint64_t)address + blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags; +- (uint32_t) producePreGapForTrack:(DRTrack*)track + intoBuffer:(char*)buffer length:(uint32_t)bufferLength + atAddress:(uint64_t)address blockSize:(uint32_t)blockSize + ioFlags:(uint32_t*)flags; +@end + +@implementation OpenDiskDevice + +- (OpenDiskDevice*) initWithDevice: (DRDevice*) device_ +{ + [super init]; + + device = device_; + tracks = [[NSMutableArray alloc] init]; + return self; +} + +- (void) dealloc +{ + [tracks release]; + [super dealloc]; +} + +- (bool) isDiskPresent +{ + return [device isValid] + && [[[device status] objectForKey: DRDeviceMediaStateKey] + isEqualTo: DRDeviceMediaStateMediaPresent]; +} + +- (int) getNumAvailableAudioBlocks +{ + return [[[[device status] objectForKey: DRDeviceMediaInfoKey] + objectForKey: DRDeviceMediaBlocksFreeKey] intValue]; +} + +- (void) addSourceTrack: (JUCE_NAMESPACE::AudioSource*) source_ numSamples: (int) numSamples_ +{ + AudioTrackProducer* p = [[AudioTrackProducer alloc] initWithAudioSource: source_ numSamples: numSamples_]; + DRTrack* t = [[DRTrack alloc] initWithProducer: p]; + [p setupTrackProperties: t]; + + [tracks addObject: t]; + + [t release]; + [p release]; +} + +- (void) burn: (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener*) listener errorString: (JUCE_NAMESPACE::String*) error + ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting +{ + DRBurn* burn = [DRBurn burnForDevice: device]; + + if (! [device acquireExclusiveAccess]) + { + *error = "Couldn't open or write to the CD device"; + return; + } + + [device acquireMediaReservation]; + + NSMutableDictionary* d = [[burn properties] mutableCopy]; + [d autorelease]; + [d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey]; + [d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey]; + [d setObject: (shouldEject ? DRBurnCompletionActionEject : DRBurnCompletionActionMount) + forKey: DRBurnCompletionActionKey]; + [burn setProperties: d]; + + [burn writeLayout: tracks]; + + for (;;) + { + JUCE_NAMESPACE::Thread::sleep (300); + float progress = [[[burn status] objectForKey: DRStatusPercentCompleteKey] floatValue]; + + if (listener != 0 && listener->audioCDBurnProgress (progress)) + { + [burn abort]; + *error = "User cancelled the write operation"; + break; + } + + if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateFailed]) + { + *error = "Write operation failed"; + break; + } + else if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateDone]) + { + break; + } + + NSString* err = (NSString*) [[[burn status] objectForKey: DRErrorStatusKey] + objectForKey: DRErrorStatusErrorStringKey]; + + if ([err length] > 0) + { + *error = JUCE_NAMESPACE::String::fromUTF8 ((JUCE_NAMESPACE::uint8*) [err UTF8String]); + break; + } + } + + [device releaseMediaReservation]; + [device releaseExclusiveAccess]; +} +@end + +@implementation AudioTrackProducer + +- (AudioTrackProducer*) init: (int) lengthInFrames_ +{ + lengthInFrames = lengthInFrames_; + readPosition = 0; + return self; +} + +- (void) setupTrackProperties: (DRTrack*) track +{ + NSMutableDictionary* p = [[track properties] mutableCopy]; + [p setObject:[DRMSF msfWithFrames: lengthInFrames] forKey: DRTrackLengthKey]; + [p setObject:[NSNumber numberWithUnsignedShort:2352] forKey: DRBlockSizeKey]; + [p setObject:[NSNumber numberWithInt:0] forKey: DRDataFormKey]; + [p setObject:[NSNumber numberWithInt:0] forKey: DRBlockTypeKey]; + [p setObject:[NSNumber numberWithInt:0] forKey: DRTrackModeKey]; + [p setObject:[NSNumber numberWithInt:0] forKey: DRSessionFormatKey]; + + [track setProperties: p]; + [p release]; +} + +- (AudioTrackProducer*) initWithAudioSource: (JUCE_NAMESPACE::AudioSource*) source_ numSamples: (int) lengthInSamples +{ + AudioTrackProducer* s = [self init: (lengthInSamples + 587) / 588]; + + if (s != nil) + s->source = source_; + + return s; +} + +- (void) dealloc +{ + if (source != 0) + { + source->releaseResources(); + delete source; + } + + [super dealloc]; +} + +- (void) cleanupTrackAfterBurn: (DRTrack*) track +{ +} + +- (BOOL) cleanupTrackAfterVerification: (DRTrack*) track +{ + return true; +} + +- (uint64_t) estimateLengthOfTrack: (DRTrack*) track +{ + return lengthInFrames; +} + +- (BOOL) prepareTrack: (DRTrack*) track forBurn: (DRBurn*) burn + toMedia: (NSDictionary*) mediaInfo +{ + if (source != 0) + source->prepareToPlay (44100 / 75, 44100); + + readPosition = 0; + return true; +} + +- (BOOL) prepareTrackForVerification: (DRTrack*) track +{ + if (source != 0) + source->prepareToPlay (44100 / 75, 44100); + + return true; +} + +- (uint32_t) produceDataForTrack: (DRTrack*) track intoBuffer: (char*) buffer + length: (uint32_t) bufferLength atAddress: (uint64_t) address + blockSize: (uint32_t) blockSize ioFlags: (uint32_t*) flags +{ + if (source != 0) + { + const int numSamples = JUCE_NAMESPACE::jmin (bufferLength / 4, (lengthInFrames * (44100 / 75)) - readPosition); + + if (numSamples > 0) + { + JUCE_NAMESPACE::AudioSampleBuffer tempBuffer (2, numSamples); + + JUCE_NAMESPACE::AudioSourceChannelInfo info; + info.buffer = &tempBuffer; + info.startSample = 0; + info.numSamples = numSamples; + + source->getNextAudioBlock (info); + + JUCE_NAMESPACE::AudioDataConverters::convertFloatToInt16LE (tempBuffer.getSampleData (0), + buffer, numSamples, 4); + JUCE_NAMESPACE::AudioDataConverters::convertFloatToInt16LE (tempBuffer.getSampleData (1), + buffer + 2, numSamples, 4); + + readPosition += numSamples; + } + + return numSamples * 4; + } + + return 0; +} + +- (uint32_t) producePreGapForTrack: (DRTrack*) track + intoBuffer: (char*) buffer length: (uint32_t) bufferLength + atAddress: (uint64_t) address blockSize: (uint32_t) blockSize + ioFlags: (uint32_t*) flags +{ + zeromem (buffer, bufferLength); + return bufferLength; +} + +- (BOOL) verifyDataForTrack: (DRTrack*) track inBuffer: (const char*) buffer + length: (uint32_t) bufferLength atAddress: (uint64_t) address + blockSize: (uint32_t) blockSize ioFlags: (uint32_t*) flags +{ + return true; +} + +@end + +BEGIN_JUCE_NAMESPACE + +AudioCDBurner::AudioCDBurner (const int deviceIndex) + : internal (0) +{ + OpenDiskDevice* dev = [[OpenDiskDevice alloc] initWithDevice: [[DRDevice devices] objectAtIndex: deviceIndex]]; + + internal = (void*) dev; +} + +AudioCDBurner::~AudioCDBurner() +{ + OpenDiskDevice* dev = (OpenDiskDevice*) internal; + + if (dev != 0) + [dev release]; +} + +AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) +{ + AudioCDBurner* b = new AudioCDBurner (deviceIndex); + + if (b->internal == 0) + deleteAndZero (b); + + return b; +} + +static NSArray* findDiskBurnerDevices() +{ + NSMutableArray* results = [NSMutableArray array]; + NSArray* devs = [DRDevice devices]; + + if (devs != 0) + { + int num = [devs count]; + int i; + for (i = 0; i < num; ++i) + { + NSDictionary* dic = [[devs objectAtIndex: i] info]; + NSString* name = [dic valueForKey: DRDeviceProductNameKey]; + if (name != nil) + [results addObject: name]; + } + } + + return results; +} + +const StringArray AudioCDBurner::findAvailableDevices() +{ + NSArray* names = findDiskBurnerDevices(); + StringArray s; + + for (unsigned int i = 0; i < [names count]; ++i) + s.add (String::fromUTF8 ((JUCE_NAMESPACE::uint8*) [[names objectAtIndex: i] UTF8String])); + + return s; +} + +bool AudioCDBurner::isDiskPresent() const +{ + OpenDiskDevice* dev = (OpenDiskDevice*) internal; + + return dev != 0 && [dev isDiskPresent]; +} + +int AudioCDBurner::getNumAvailableAudioBlocks() const +{ + OpenDiskDevice* dev = (OpenDiskDevice*) internal; + + return [dev getNumAvailableAudioBlocks]; +} + +bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps) +{ + OpenDiskDevice* dev = (OpenDiskDevice*) internal; + + if (dev != 0) + { + [dev addSourceTrack: source numSamples: numSamps]; + return true; + } + + return false; +} + +const String AudioCDBurner::burn (JUCE_NAMESPACE::AudioCDBurner::BurnProgressListener* listener, + const bool ejectDiscAfterwards, + const bool peformFakeBurnForTesting) +{ + String error ("Couldn't open or write to the CD device"); + + OpenDiskDevice* dev = (OpenDiskDevice*) internal; + + if (dev != 0) + { + error = String::empty; + [dev burn: listener + errorString: &error + ejectAfterwards: ejectDiscAfterwards + isFake: peformFakeBurnForTesting]; + } + + return error; +} + +void AudioCDReader::ejectDisk() +{ + const ScopedAutoReleasePool p; + [[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath: juceStringToNS (volumeDir.getFullPathName())]; +} + +#endif +/********* End of inlined file: juce_mac_AudioCDBurner.mm *********/ + +/********* Start of inlined file: juce_mac_Fonts.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +class FontHelper +{ + NSFont* font; + +public: + String name; + bool isBold, isItalic, needsItalicTransform; + float fontSize, totalSize, ascent; + int refCount; + NSMutableDictionary* attributes; + + FontHelper (const String& name_, + const bool bold_, + const bool italic_, + const float size_) + : font (0), + name (name_), + isBold (bold_), + isItalic (italic_), + needsItalicTransform (false), + fontSize (size_), + refCount (1) + { + attributes = [[NSMutableDictionary dictionaryWithObject: [NSNumber numberWithInt: 0] + forKey: NSLigatureAttributeName] retain]; + + font = [NSFont fontWithName: juceStringToNS (name_) size: size_]; + + if (italic_) + { + NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSItalicFontMask]; + + if (newFont == font) + needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. + + font = newFont; + } + + if (bold_) + font = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSBoldFontMask]; + + [font retain]; + + ascent = fabsf ([font ascender]); + totalSize = ascent + fabsf ([font descender]); + } + + ~FontHelper() + { + [font release]; + [attributes release]; + } + + bool getPathAndKerning (const juce_wchar char1, + const juce_wchar char2, + Path* path, + float& kerning, + float* ascent, + float* descent) + { + const ScopedAutoReleasePool pool; + + if (font == 0 + || ! [[font coveredCharacterSet] longCharacterIsMember: (UTF32Char) char1]) + return false; + + String chars; + chars << ' ' << char1 << char2; + + NSTextStorage* textStorage = [[[NSTextStorage alloc] initWithString: juceStringToNS (chars) + attributes: attributes] autorelease]; + NSLayoutManager* layoutManager = [[[NSLayoutManager alloc] init] autorelease]; + NSTextContainer* textContainer = [[[NSTextContainer alloc] init] autorelease]; + [layoutManager addTextContainer: textContainer]; + [textStorage addLayoutManager: layoutManager]; + [textStorage setFont: font]; + + unsigned int glyphIndex = [layoutManager glyphRangeForCharacterRange: NSMakeRange (1, 1) + actualCharacterRange: 0].location; + NSPoint p1 = [layoutManager locationForGlyphAtIndex: glyphIndex]; + NSPoint p2 = [layoutManager locationForGlyphAtIndex: glyphIndex + 1]; + kerning = p2.x - p1.x; + + if (ascent != 0) + *ascent = this->ascent; + + if (descent != 0) + *descent = fabsf ([font descender]); + + if (path != 0) + { + NSBezierPath* bez = [NSBezierPath bezierPath]; + [bez moveToPoint: NSMakePoint (0, 0)]; + [bez appendBezierPathWithGlyph: [layoutManager glyphAtIndex: glyphIndex] + inFont: font]; + + for (int i = 0; i < [bez elementCount]; ++i) + { + NSPoint p[3]; + switch ([bez elementAtIndex: i associatedPoints: p]) + { + case NSMoveToBezierPathElement: + path->startNewSubPath (p[0].x, -p[0].y); + break; + case NSLineToBezierPathElement: + path->lineTo (p[0].x, -p[0].y); + break; + case NSCurveToBezierPathElement: + path->cubicTo (p[0].x, -p[0].y, p[1].x, -p[1].y, p[2].x, -p[2].y); + break; + case NSClosePathBezierPathElement: + path->closeSubPath(); + break; + default: + jassertfalse + break; + } + } + + if (needsItalicTransform) + path->applyTransform (AffineTransform::identity.sheared (-0.15, 0)); + } + + return kerning != 0; + } + + juce_wchar getDefaultChar() + { + return 0; + } +}; + +class FontHelperCache : public Timer, + public DeletedAtShutdown +{ + VoidArray cache; + +public: + FontHelperCache() + { + } + + ~FontHelperCache() + { + for (int i = cache.size(); --i >= 0;) + { + FontHelper* const f = (FontHelper*) cache.getUnchecked(i); + delete f; + } + + clearSingletonInstance(); + } + + FontHelper* getFont (const String& name, + const bool bold, + const bool italic, + const float size = 1024) + { + for (int i = cache.size(); --i >= 0;) + { + FontHelper* const f = (FontHelper*) cache.getUnchecked(i); + + if (f->name == name + && f->isBold == bold + && f->isItalic == italic + && f->fontSize == size) + { + f->refCount++; + return f; + } + } + + FontHelper* const f = new FontHelper (name, bold, italic, size); + cache.add (f); + return f; + } + + void releaseFont (FontHelper* f) + { + for (int i = cache.size(); --i >= 0;) + { + FontHelper* const f2 = (FontHelper*) cache.getUnchecked(i); + + if (f == f2) + { + f->refCount--; + + if (f->refCount == 0) + startTimer (5000); + + break; + } + } + } + + void timerCallback() + { + stopTimer(); + + for (int i = cache.size(); --i >= 0;) + { + FontHelper* const f = (FontHelper*) cache.getUnchecked(i); + + if (f->refCount == 0) + { + cache.remove (i); + delete f; + } + } + + if (cache.size() == 0) + delete this; + } + + juce_DeclareSingleton_SingleThreaded_Minimal (FontHelperCache) +}; + +juce_ImplementSingleton_SingleThreaded (FontHelperCache) + +void Typeface::initialiseTypefaceCharacteristics (const String& fontName, + bool bold, + bool italic, + bool addAllGlyphsToFont) throw() +{ + // This method is only safe to be called from the normal UI thread.. + jassert (MessageManager::getInstance()->isThisTheMessageThread()); + + FontHelper* const helper = FontHelperCache::getInstance() + ->getFont (fontName, bold, italic); + + clear(); + setAscent (helper->ascent / helper->totalSize); + setName (fontName); + setDefaultCharacter (helper->getDefaultChar()); + setBold (bold); + setItalic (italic); + + if (addAllGlyphsToFont) + { + //xxx + jassertfalse + } + + FontHelperCache::getInstance()->releaseFont (helper); +} + +bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() +{ + // This method is only safe to be called from the normal UI thread.. + jassert (MessageManager::getInstance()->isThisTheMessageThread()); + + if (character == 0) + return false; + + FontHelper* const helper = FontHelperCache::getInstance() + ->getFont (getName(), isBold(), isItalic()); + + Path path; + float width; + bool foundOne = false; + + if (helper->getPathAndKerning (character, T('I'), &path, width, 0, 0)) + { + path.applyTransform (AffineTransform::scale (1.0f / helper->totalSize, + 1.0f / helper->totalSize)); + + addGlyph (character, path, width / helper->totalSize); + + for (int i = 0; i < glyphs.size(); ++i) + { + const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); + + float kerning; + if (helper->getPathAndKerning (character, g->getCharacter(), 0, kerning, 0, 0)) + { + kerning = (kerning - width) / helper->totalSize; + + if (kerning != 0) + addKerningPair (character, g->getCharacter(), kerning); + } + + if (helper->getPathAndKerning (g->getCharacter(), character, 0, kerning, 0, 0)) + { + kerning = kerning / helper->totalSize - g->width; + + if (kerning != 0) + addKerningPair (g->getCharacter(), character, kerning); + } + } + + foundOne = true; + } + + FontHelperCache::getInstance()->releaseFont (helper); + return foundOne; +} + +const StringArray Font::findAllTypefaceNames() throw() +{ + StringArray names; + + const ScopedAutoReleasePool pool; + NSArray* fonts = [[NSFontManager sharedFontManager] availableFontFamilies]; + + for (unsigned int i = 0; i < [fonts count]; ++i) + names.add (nsStringToJuce ((NSString*) [fonts objectAtIndex: i])); + + names.sort (true); + return names; +} + +void Typeface::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() +{ + defaultSans = "Lucida Grande"; + defaultSerif = "Times New Roman"; + defaultFixed = "Monaco"; +} + +#endif +/********* End of inlined file: juce_mac_Fonts.mm *********/ + +/********* Start of inlined file: juce_mac_MessageManager.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +struct CallbackMessagePayload +{ + MessageCallbackFunction* function; + void* parameter; + void* volatile result; + bool volatile hasBeenExecuted; +}; + +/* When you use multiple DLLs which share similarly-named obj-c classes - like + for example having more than one juce plugin loaded into a host, then when a + method is called, the actual code that runs might actually be in a different module + than the one you expect... So any calls to library functions or statics that are + made inside obj-c methods will probably end up getting executed in a different DLL's + memory space. Not a great thing to happen - this obviously leads to bizarre crashes. + + To work around this insanity, I'm only allowing obj-c methods to make calls to + virtual methods of an object that's known to live inside the right module's space. +*/ +class AppDelegateRedirector +{ +public: + AppDelegateRedirector() {} + virtual ~AppDelegateRedirector() {} + + virtual NSApplicationTerminateReply shouldTerminate() + { + if (JUCEApplication::getInstance() != 0) + { + JUCEApplication::getInstance()->systemRequestedQuit(); + return NSTerminateCancel; + } + + return NSTerminateNow; + } + + virtual BOOL openFile (const NSString* filename) + { + if (JUCEApplication::getInstance() != 0) + { + JUCEApplication::getInstance()->anotherInstanceStarted (nsStringToJuce (filename)); + return YES; + } + + return NO; + } + + virtual void openFiles (NSArray* filenames) + { + StringArray files; + for (unsigned int i = 0; i < [filenames count]; ++i) + files.add (nsStringToJuce ((NSString*) [filenames objectAtIndex: i])); + + if (files.size() > 0 && JUCEApplication::getInstance() != 0) + { + JUCEApplication::getInstance()->anotherInstanceStarted (files.joinIntoString (T(" "))); + } + } + + virtual void focusChanged() + { + juce_HandleProcessFocusChange(); + } + + virtual void deliverMessage (void* message) + { + // no need for an mm lock here - deliverMessage locks it + MessageManager::getInstance()->deliverMessage (message); + } + + virtual void performCallback (CallbackMessagePayload* pl) + { + pl->result = (*pl->function) (pl->parameter); + pl->hasBeenExecuted = true; + } + + virtual void deleteSelf() + { + delete this; + } +}; + +END_JUCE_NAMESPACE +using namespace JUCE_NAMESPACE; + +#define JuceAppDelegate MakeObjCClassName(JuceAppDelegate) + +static int numPendingMessages = 0; + +@interface JuceAppDelegate : NSObject +{ +@private + id oldDelegate; + AppDelegateRedirector* redirector; + +@public + bool flushingMessages; +} + +- (JuceAppDelegate*) init; +- (void) dealloc; +- (BOOL) application: (NSApplication*) theApplication openFile: (NSString*) filename; +- (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames; +- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app; +- (void) applicationDidBecomeActive: (NSNotification*) aNotification; +- (void) applicationDidResignActive: (NSNotification*) aNotification; +- (void) applicationWillUnhide: (NSNotification*) aNotification; +- (void) customEvent: (id) data; +- (void) performCallback: (id) info; +- (void) dummyMethod; +@end + +@implementation JuceAppDelegate + +- (JuceAppDelegate*) init +{ + [super init]; + + redirector = new AppDelegateRedirector(); + numPendingMessages = 0; + flushingMessages = false; + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + + if (JUCEApplication::getInstance() != 0) + { + oldDelegate = [NSApp delegate]; + [NSApp setDelegate: self]; + } + else + { + oldDelegate = 0; + [center addObserver: self selector: @selector (applicationDidResignActive:) + name: NSApplicationDidResignActiveNotification object: NSApp]; + + [center addObserver: self selector: @selector (applicationDidBecomeActive:) + name: NSApplicationDidBecomeActiveNotification object: NSApp]; + + [center addObserver: self selector: @selector (applicationWillUnhide:) + name: NSApplicationWillUnhideNotification object: NSApp]; + } + + return self; +} + +- (void) dealloc +{ + if (oldDelegate != 0) + [NSApp setDelegate: oldDelegate]; + + redirector->deleteSelf(); + [super dealloc]; +} + +- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app +{ + return redirector->shouldTerminate(); +} + +- (BOOL) application: (NSApplication*) app openFile: (NSString*) filename +{ + return redirector->openFile (filename); +} + +- (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames +{ + return redirector->openFiles (filenames); +} + +- (void) applicationDidBecomeActive: (NSNotification*) aNotification +{ + redirector->focusChanged(); +} + +- (void) applicationDidResignActive: (NSNotification*) aNotification +{ + redirector->focusChanged(); +} + +- (void) applicationWillUnhide: (NSNotification*) aNotification +{ + redirector->focusChanged(); +} + +- (void) customEvent: (id) n +{ + atomicDecrement (numPendingMessages); + + NSData* data = (NSData*) n; + void* message = 0; + [data getBytes: &message length: sizeof (message)]; + [data release]; + + if (message != 0 && ! flushingMessages) + redirector->deliverMessage (message); +} + +- (void) performCallback: (id) info +{ + if ([info isKindOfClass: [NSData class]]) + { + CallbackMessagePayload* pl = (CallbackMessagePayload*) [((NSData*) info) bytes]; + + if (pl != 0) + redirector->performCallback (pl); + } + else + { + jassertfalse // should never get here! + } +} + +- (void) dummyMethod {} // (used as a way of running a dummy thread) + +@end + +BEGIN_JUCE_NAMESPACE + +static JuceAppDelegate* juceAppDelegate = 0; + +void MessageManager::runDispatchLoop() +{ + if (! quitMessagePosted) // check that the quit message wasn't already posted.. + { + const ScopedAutoReleasePool pool; + + // must only be called by the message thread! + jassert (isThisTheMessageThread()); + + [NSApp run]; + } +} + +void MessageManager::stopDispatchLoop() +{ + quitMessagePosted = true; + [NSApp stop: nil]; + [NSApp activateIgnoringOtherApps: YES]; // (if the app is inactive, it sits there and ignores the quit request until the next time it gets activated) + [NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1]; +} + +static bool isEventBlockedByModalComps (NSEvent* e) +{ + if (Component::getNumCurrentlyModalComponents() == 0) + return false; + + NSWindow* const w = [e window]; + if (w == 0 || [w worksWhenModal]) + return false; + + bool isKey = false, isInputAttempt = false; + + switch ([e type]) + { + case NSKeyDown: + case NSKeyUp: + isKey = isInputAttempt = true; + break; + + case NSLeftMouseDown: + case NSRightMouseDown: + case NSOtherMouseDown: + isInputAttempt = true; + break; + + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSLeftMouseUp: + case NSRightMouseUp: + case NSOtherMouseUp: + case NSOtherMouseDragged: + if (Component::getComponentUnderMouse() != 0) + return false; + break; + + case NSMouseMoved: + case NSMouseEntered: + case NSMouseExited: + case NSCursorUpdate: + case NSScrollWheel: + case NSTabletPoint: + case NSTabletProximity: + break; + + default: + return false; + } + + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + { + ComponentPeer* const peer = ComponentPeer::getPeer (i); + NSView* const compView = (NSView*) peer->getNativeHandle(); + + if ([compView window] == w) + { + if (isKey) + { + if (compView == [w firstResponder]) + return false; + } + else + { + if (NSPointInRect ([compView convertPoint: [e locationInWindow] fromView: nil], + [compView bounds])) + return false; + } + } + } + + if (isInputAttempt) + { + if (! [NSApp isActive]) + [NSApp activateIgnoringOtherApps: YES]; + + Component* const modal = Component::getCurrentlyModalComponent (0); + if (modal != 0) + modal->inputAttemptWhenModal(); + } + + return true; +} + +bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) +{ + const ScopedAutoReleasePool pool; + jassert (isThisTheMessageThread()); // must only be called by the message thread + + uint32 endTime = Time::getMillisecondCounter() + millisecondsToRunFor; + NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001]; + + while (! quitMessagePosted) + { + const ScopedAutoReleasePool pool; + + [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode + beforeDate: endDate]; + + NSEvent* e = [NSApp nextEventMatchingMask: NSAnyEventMask + untilDate: endDate + inMode: NSDefaultRunLoopMode + dequeue: YES]; + + if (e != 0 && ! isEventBlockedByModalComps (e)) + [NSApp sendEvent: e]; + + if (Time::getMillisecondCounter() >= endTime) + break; + } + + return ! quitMessagePosted; +} + +void MessageManager::doPlatformSpecificInitialisation() +{ + if (juceAppDelegate == 0) + juceAppDelegate = [[JuceAppDelegate alloc] init]; + + // This launches a dummy thread, which forces Cocoa to initialise NSThreads + // correctly (needed prior to 10.5) + if (! [NSThread isMultiThreaded]) + [NSThread detachNewThreadSelector: @selector (dummyMethod) + toTarget: juceAppDelegate + withObject: nil]; + + initialiseMainMenu(); +} + +void MessageManager::doPlatformSpecificShutdown() +{ + if (juceAppDelegate != 0) + { + [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceAppDelegate]; + [[NSNotificationCenter defaultCenter] removeObserver: juceAppDelegate]; + + // Annoyingly, cancelPerformSelectorsWithTarget can't actually cancel the messages + // sent by performSelectorOnMainThread, so need to manually flush these before quitting.. + juceAppDelegate->flushingMessages = true; + + for (int i = 100; --i >= 0 && numPendingMessages > 0;) + { + const ScopedAutoReleasePool pool; + [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode + beforeDate: [NSDate dateWithTimeIntervalSinceNow: 5 * 0.001]]; + } + + [juceAppDelegate release]; + juceAppDelegate = 0; + } +} + +bool juce_postMessageToSystemQueue (void* message) +{ + atomicIncrement (numPendingMessages); + + [juceAppDelegate performSelectorOnMainThread: @selector (customEvent:) + withObject: (id) [[NSData alloc] initWithBytes: &message length: (int) sizeof (message)] + waitUntilDone: NO]; + return true; +} + +void MessageManager::broadcastMessage (const String& value) throw() +{ +} + +void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* callback, + void* data) +{ + if (isThisTheMessageThread()) + { + return (*callback) (data); + } + else + { + // If a thread has a MessageManagerLock and then tries to call this method, it'll + // deadlock because the message manager is blocked from running, so can never + // call your function.. + jassert (! MessageManager::getInstance()->currentThreadHasLockedMessageManager()); + + const ScopedAutoReleasePool pool; + + CallbackMessagePayload cmp; + cmp.function = callback; + cmp.parameter = data; + cmp.result = 0; + cmp.hasBeenExecuted = false; + + [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:) + withObject: [NSData dataWithBytesNoCopy: &cmp + length: sizeof (cmp) + freeWhenDone: NO] + waitUntilDone: YES]; + + return cmp.result; + } +} + +#endif +/********* End of inlined file: juce_mac_MessageManager.mm *********/ + +/********* Start of inlined file: juce_mac_WebBrowserComponent.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE && JUCE_WEB_BROWSER + +END_JUCE_NAMESPACE + +#define DownloadClickDetector MakeObjCClassName(DownloadClickDetector) + +@interface DownloadClickDetector : NSObject +{ + JUCE_NAMESPACE::WebBrowserComponent* ownerComponent; +} + +- (DownloadClickDetector*) initWithWebBrowserOwner: (JUCE_NAMESPACE::WebBrowserComponent*) ownerComponent; + +- (void) webView: (WebView*) webView decidePolicyForNavigationAction: (NSDictionary*) actionInformation + request: (NSURLRequest*) request + frame: (WebFrame*) frame + decisionListener: (id) listener; +@end + +@implementation DownloadClickDetector + +- (DownloadClickDetector*) initWithWebBrowserOwner: (JUCE_NAMESPACE::WebBrowserComponent*) ownerComponent_ +{ + [super init]; + ownerComponent = ownerComponent_; + return self; +} + +- (void) webView: (WebView*) sender decidePolicyForNavigationAction: (NSDictionary*) actionInformation + request: (NSURLRequest*) request + frame: (WebFrame*) frame + decisionListener: (id ) listener +{ + NSURL* url = [actionInformation valueForKey: @"WebActionOriginalURLKey"]; + + if (ownerComponent->pageAboutToLoad (nsStringToJuce ([url absoluteString]))) + [listener use]; + else + [listener ignore]; +} + +@end + +BEGIN_JUCE_NAMESPACE + +class WebBrowserComponentInternal : public NSViewComponent +{ +public: + WebBrowserComponentInternal (WebBrowserComponent* owner) + { + webView = [[WebView alloc] initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f) + frameName: @"" + groupName: @""]; + setView (webView); + + clickListener = [[DownloadClickDetector alloc] initWithWebBrowserOwner: owner]; + [webView setPolicyDelegate: clickListener]; + } + + ~WebBrowserComponentInternal() + { + [webView setPolicyDelegate: nil]; + [clickListener release]; + setView (0); + } + + void goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) + { + NSMutableURLRequest* r + = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (url)] + cachePolicy: NSURLRequestUseProtocolCachePolicy + timeoutInterval: 30.0]; + + if (postData != 0 && postData->getSize() > 0) + { + [r setHTTPMethod: @"POST"]; + [r setHTTPBody: [NSData dataWithBytes: postData->getData() + length: postData->getSize()]]; + } + + if (headers != 0) + { + for (int i = 0; i < headers->size(); ++i) + { + const String headerName ((*headers)[i].upToFirstOccurrenceOf (T(":"), false, false).trim()); + const String headerValue ((*headers)[i].fromFirstOccurrenceOf (T(":"), false, false).trim()); + + [r setValue: juceStringToNS (headerValue) + forHTTPHeaderField: juceStringToNS (headerName)]; + } + } + + stop(); + [[webView mainFrame] loadRequest: r]; + } + + void goBack() + { + [webView goBack]; + } + + void goForward() + { + [webView goForward]; + } + + void stop() + { + [webView stopLoading: nil]; + } + + void refresh() + { + [webView reload: nil]; + } + +private: + WebView* webView; + DownloadClickDetector* clickListener; +}; + +WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_) + : browser (0), + blankPageShown (false), + unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_) +{ + setOpaque (true); + + addAndMakeVisible (browser = new WebBrowserComponentInternal (this)); +} + +WebBrowserComponent::~WebBrowserComponent() +{ + deleteAndZero (browser); +} + +void WebBrowserComponent::goToURL (const String& url, + const StringArray* headers, + const MemoryBlock* postData) +{ + lastURL = url; + + lastHeaders.clear(); + if (headers != 0) + lastHeaders = *headers; + + lastPostData.setSize (0); + if (postData != 0) + lastPostData = *postData; + + blankPageShown = false; + + browser->goToURL (url, headers, postData); +} + +void WebBrowserComponent::stop() +{ + browser->stop(); +} + +void WebBrowserComponent::goBack() +{ + lastURL = String::empty; + blankPageShown = false; + browser->goBack(); +} + +void WebBrowserComponent::goForward() +{ + lastURL = String::empty; + browser->goForward(); +} + +void WebBrowserComponent::refresh() +{ + browser->refresh(); +} + +void WebBrowserComponent::paint (Graphics& g) +{ +} + +void WebBrowserComponent::checkWindowAssociation() +{ + if (isShowing()) + { + if (blankPageShown) + goBack(); + } + else + { + if (unloadPageWhenBrowserIsHidden && ! blankPageShown) + { + // when the component becomes invisible, some stuff like flash + // carries on playing audio, so we need to force it onto a blank + // page to avoid this, (and send it back when it's made visible again). + + blankPageShown = true; + browser->goToURL ("about:blank", 0, 0); + } + } +} + +void WebBrowserComponent::reloadLastURL() +{ + if (lastURL.isNotEmpty()) + { + goToURL (lastURL, &lastHeaders, &lastPostData); + lastURL = String::empty; + } +} + +void WebBrowserComponent::parentHierarchyChanged() +{ + checkWindowAssociation(); +} + +void WebBrowserComponent::resized() +{ + browser->setSize (getWidth(), getHeight()); +} + +void WebBrowserComponent::visibilityChanged() +{ + checkWindowAssociation(); +} + +bool WebBrowserComponent::pageAboutToLoad (const String& url) +{ + return true; +} + +#endif +/********* End of inlined file: juce_mac_WebBrowserComponent.mm *********/ + +/********* Start of inlined file: juce_mac_CoreAudio.cpp *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +#ifndef JUCE_COREAUDIO_ERROR_LOGGING_ENABLED + #define JUCE_COREAUDIO_ERROR_LOGGING_ENABLED 1 +#endif + +#undef log +#if JUCE_COREAUDIO_LOGGING_ENABLED + #define log(a) Logger::writeToLog (a) +#else + #define log(a) +#endif + +#undef OK +#if JUCE_COREAUDIO_ERROR_LOGGING_ENABLED + static bool logAnyErrors_CoreAudio (const OSStatus err, const int lineNum) + { + if (err == noErr) + return true; + + Logger::writeToLog (T("CoreAudio error: ") + String (lineNum) + T(" - ") + String::toHexString ((int)err)); + jassertfalse + return false; + } + + #define OK(a) logAnyErrors_CoreAudio (a, __LINE__) +#else + #define OK(a) (a == noErr) +#endif + +class CoreAudioInternal : public Timer +{ +public: + + CoreAudioInternal (AudioDeviceID id) + : inputLatency (0), + outputLatency (0), + callback (0), +#if ! MACOS_10_4_OR_EARLIER + audioProcID (0), +#endif + inputDevice (0), + isSlaveDevice (false), + deviceID (id), + started (false), + sampleRate (0), + bufferSize (512), + audioBuffer (0), + numInputChans (0), + numOutputChans (0), + callbacksAllowed (true), + numInputChannelInfos (0), + numOutputChannelInfos (0), + tempInputBuffers (0), + tempOutputBuffers (0), + inputChannelInfo (0), + outputChannelInfo (0) + { + jassert (deviceID != 0); + + updateDetailsFromDevice(); + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioObjectPropertySelectorWildcard; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; + + AudioObjectAddPropertyListener (deviceID, &pa, deviceListenerProc, this); + } + + ~CoreAudioInternal() + { + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioObjectPropertySelectorWildcard; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; + + AudioObjectRemovePropertyListener (deviceID, &pa, deviceListenerProc, this); + + stop (false); + delete inputDevice; + + juce_free (audioBuffer); + juce_free (tempInputBuffers); + juce_free (tempOutputBuffers); + juce_free (inputChannelInfo); + juce_free (outputChannelInfo); + } + + void allocateTempBuffers() + { + const int tempBufSize = bufferSize + 4; + juce_free (audioBuffer); + audioBuffer = (float*) juce_calloc ((numInputChans + numOutputChans) * tempBufSize * sizeof (float)); + + juce_free (tempInputBuffers); + tempInputBuffers = (float**) juce_calloc (sizeof (float*) * (numInputChans + 2)); + juce_free (tempOutputBuffers); + tempOutputBuffers = (float**) juce_calloc (sizeof (float*) * (numOutputChans + 2)); + + int i, count = 0; + for (i = 0; i < numInputChans; ++i) + tempInputBuffers[i] = audioBuffer + count++ * tempBufSize; + + for (i = 0; i < numOutputChans; ++i) + tempOutputBuffers[i] = audioBuffer + count++ * tempBufSize; + } + + // returns the number of actual available channels + void fillInChannelInfo (const bool input) + { + int chanNum = 0; + UInt32 size; + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyStreamConfiguration; + pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + pa.mElement = kAudioObjectPropertyElementMaster; + + if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) + { + AudioBufferList* const bufList = (AudioBufferList*) juce_calloc (size); + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, bufList))) + { + const int numStreams = bufList->mNumberBuffers; + + for (int i = 0; i < numStreams; ++i) + { + const AudioBuffer& b = bufList->mBuffers[i]; + + for (unsigned int j = 0; j < b.mNumberChannels; ++j) + { + String name; + + { + uint8 channelName [256]; + zerostruct (channelName); + UInt32 nameSize = sizeof (channelName); + UInt32 channelNum = chanNum + 1; + pa.mSelector = kAudioDevicePropertyChannelName; + + if (AudioObjectGetPropertyData (deviceID, &pa, sizeof (channelNum), &channelNum, &nameSize, channelName) == noErr) + name = String::fromUTF8 (channelName, nameSize); + } + + if (input) + { + if (activeInputChans[chanNum]) + { + inputChannelInfo [numInputChannelInfos].streamNum = i; + inputChannelInfo [numInputChannelInfos].dataOffsetSamples = j; + inputChannelInfo [numInputChannelInfos].dataStrideSamples = b.mNumberChannels; + ++numInputChannelInfos; + } + + if (name.isEmpty()) + name << "Input " << (chanNum + 1); + + inChanNames.add (name); + } + else + { + if (activeOutputChans[chanNum]) + { + outputChannelInfo [numOutputChannelInfos].streamNum = i; + outputChannelInfo [numOutputChannelInfos].dataOffsetSamples = j; + outputChannelInfo [numOutputChannelInfos].dataStrideSamples = b.mNumberChannels; + ++numOutputChannelInfos; + } + + if (name.isEmpty()) + name << "Output " << (chanNum + 1); + + outChanNames.add (name); + } + + ++chanNum; + } + } + } + + juce_free (bufList); + } + } + + void updateDetailsFromDevice() + { + stopTimer(); + + if (deviceID == 0) + return; + + const ScopedLock sl (callbackLock); + + Float64 sr; + UInt32 size = sizeof (Float64); + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyNominalSampleRate; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &sr))) + sampleRate = sr; + + UInt32 framesPerBuf; + size = sizeof (framesPerBuf); + + pa.mSelector = kAudioDevicePropertyBufferFrameSize; + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &framesPerBuf))) + { + bufferSize = framesPerBuf; + allocateTempBuffers(); + } + + bufferSizes.clear(); + + pa.mSelector = kAudioDevicePropertyBufferFrameSizeRange; + + if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) + { + AudioValueRange* ranges = (AudioValueRange*) juce_calloc (size); + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ranges))) + { + bufferSizes.add ((int) ranges[0].mMinimum); + + for (int i = 32; i < 8192; i += 32) + { + for (int j = size / sizeof (AudioValueRange); --j >= 0;) + { + if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) + { + bufferSizes.addIfNotAlreadyThere (i); + break; + } + } + } + + if (bufferSize > 0) + bufferSizes.addIfNotAlreadyThere (bufferSize); + } + + juce_free (ranges); + } + + if (bufferSizes.size() == 0 && bufferSize > 0) + bufferSizes.add (bufferSize); + + sampleRates.clear(); + const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; + String rates; + + pa.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; + + if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) + { + AudioValueRange* ranges = (AudioValueRange*) juce_calloc (size); + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ranges))) + { + for (int i = 0; i < numElementsInArray (possibleRates); ++i) + { + bool ok = false; + + for (int j = size / sizeof (AudioValueRange); --j >= 0;) + if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2) + ok = true; + + if (ok) + { + sampleRates.add (possibleRates[i]); + rates << possibleRates[i] << T(" "); + } + } + } + + juce_free (ranges); + } + + if (sampleRates.size() == 0 && sampleRate > 0) + { + sampleRates.add (sampleRate); + rates << sampleRate; + } + + log (T("sr: ") + rates); + + inputLatency = 0; + outputLatency = 0; + UInt32 lat; + size = sizeof (lat); + pa.mSelector = kAudioDevicePropertyLatency; + pa.mScope = kAudioDevicePropertyScopeInput; + //if (AudioDeviceGetProperty (deviceID, 0, true, kAudioDevicePropertyLatency, &size, &lat) == noErr) + if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat) == noErr) + inputLatency = (int) lat; + + pa.mScope = kAudioDevicePropertyScopeOutput; + size = sizeof (lat); + + if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat) == noErr) + outputLatency = (int) lat; + + log (T("lat: ") + String (inputLatency) + T(" ") + String (outputLatency)); + + inChanNames.clear(); + outChanNames.clear(); + + juce_free (inputChannelInfo); + inputChannelInfo = (CallbackDetailsForChannel*) juce_calloc (sizeof (CallbackDetailsForChannel) * (numInputChans + 2)); + numInputChannelInfos = 0; + + juce_free (outputChannelInfo); + outputChannelInfo = (CallbackDetailsForChannel*) juce_calloc (sizeof (CallbackDetailsForChannel) * (numOutputChans + 2)); + numOutputChannelInfos = 0; + + fillInChannelInfo (true); + fillInChannelInfo (false); + } + + const StringArray getSources (bool input) + { + StringArray s; + int num = 0; + OSType* types = getAllDataSourcesForDevice (deviceID, input, num); + + if (types != 0) + { + for (int i = 0; i < num; ++i) + { + AudioValueTranslation avt; + char buffer[256]; + + avt.mInputData = (void*) &(types[i]); + avt.mInputDataSize = sizeof (UInt32); + avt.mOutputData = buffer; + avt.mOutputDataSize = 256; + + UInt32 transSize = sizeof (avt); + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyDataSourceNameForID; + pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + pa.mElement = kAudioObjectPropertyElementMaster; + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &transSize, &avt))) + { + DBG (buffer); + s.add (buffer); + } + } + + juce_free (types); + } + + return s; + } + + int getCurrentSourceIndex (bool input) const + { + OSType currentSourceID = 0; + UInt32 size = sizeof (currentSourceID); + int result = -1; + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyDataSource; + pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + pa.mElement = kAudioObjectPropertyElementMaster; + + if (deviceID != 0) + { + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ¤tSourceID))) + { + int num = 0; + OSType* const types = getAllDataSourcesForDevice (deviceID, input, num); + + if (types != 0) + { + for (int i = 0; i < num; ++i) + { + if (types[num] == currentSourceID) + { + result = i; + break; + } + } + + juce_free (types); + } + } + } + + return result; + } + + void setCurrentSourceIndex (int index, bool input) + { + if (deviceID != 0) + { + int num = 0; + OSType* types = getAllDataSourcesForDevice (deviceID, input, num); + + if (types != 0) + { + if (((unsigned int) index) < (unsigned int) num) + { + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyDataSource; + pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + pa.mElement = kAudioObjectPropertyElementMaster; + + OSType typeId = types[index]; + + OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (typeId), &typeId)); + } + + juce_free (types); + } + } + } + + const String reopen (const BitArray& inputChannels, + const BitArray& outputChannels, + double newSampleRate, + int bufferSizeSamples) + { + String error; + log ("CoreAudio reopen"); + callbacksAllowed = false; + stopTimer(); + + stop (false); + + activeInputChans = inputChannels; + activeInputChans.setRange (inChanNames.size(), + activeInputChans.getHighestBit() + 1 - inChanNames.size(), + false); + + activeOutputChans = outputChannels; + activeOutputChans.setRange (outChanNames.size(), + activeOutputChans.getHighestBit() + 1 - outChanNames.size(), + false); + + numInputChans = activeInputChans.countNumberOfSetBits(); + numOutputChans = activeOutputChans.countNumberOfSetBits(); + + // set sample rate + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyNominalSampleRate; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; + Float64 sr = newSampleRate; + + if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (sr), &sr))) + { + error = "Couldn't change sample rate"; + } + else + { + // change buffer size + UInt32 framesPerBuf = bufferSizeSamples; + pa.mSelector = kAudioDevicePropertyBufferFrameSize; + + if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (framesPerBuf), &framesPerBuf))) + { + error = "Couldn't change buffer size"; + } + else + { + // Annoyingly, after changing the rate and buffer size, some devices fail to + // correctly report their new settings until some random time in the future, so + // after calling updateDetailsFromDevice, we need to manually bodge these values + // to make sure we're using the correct numbers.. + updateDetailsFromDevice(); + sampleRate = newSampleRate; + bufferSize = bufferSizeSamples; + + if (sampleRates.size() == 0) + error = "Device has no available sample-rates"; + else if (bufferSizes.size() == 0) + error = "Device has no available buffer-sizes"; + else if (inputDevice != 0) + error = inputDevice->reopen (inputChannels, + outputChannels, + newSampleRate, + bufferSizeSamples); + } + } + + callbacksAllowed = true; + return error; + } + + bool start (AudioIODeviceCallback* cb) + { + if (! started) + { + callback = 0; + + if (deviceID != 0) + { +#if MACOS_10_4_OR_EARLIER + if (OK (AudioDeviceAddIOProc (deviceID, audioIOProc, (void*) this))) +#else + if (OK (AudioDeviceCreateIOProcID (deviceID, audioIOProc, (void*) this, &audioProcID))) +#endif + { + if (OK (AudioDeviceStart (deviceID, audioIOProc))) + { + started = true; + } + else + { +#if MACOS_10_4_OR_EARLIER + OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); +#else + OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID)); + audioProcID = 0; +#endif + } + } + } + } + + if (started) + { + const ScopedLock sl (callbackLock); + callback = cb; + } + + if (inputDevice != 0) + return started && inputDevice->start (cb); + else + return started; + } + + void stop (bool leaveInterruptRunning) + { + callbackLock.enter(); + callback = 0; + callbackLock.exit(); + + if (started + && (deviceID != 0) + && ! leaveInterruptRunning) + { + OK (AudioDeviceStop (deviceID, audioIOProc)); + +#if MACOS_10_4_OR_EARLIER + OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc)); +#else + OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID)); + audioProcID = 0; +#endif + started = false; + + callbackLock.enter(); + callbackLock.exit(); + + // wait until it's definately stopped calling back.. + for (int i = 40; --i >= 0;) + { + Thread::sleep (50); + + UInt32 running = 0; + UInt32 size = sizeof (running); + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyDeviceIsRunning; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; + + OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &running)); + + if (running == 0) + break; + } + + callbackLock.enter(); + callbackLock.exit(); + } + + if (inputDevice != 0) + inputDevice->stop (leaveInterruptRunning); + } + + double getSampleRate() const + { + return sampleRate; + } + + int getBufferSize() const + { + return bufferSize; + } + + void audioCallback (const AudioBufferList* inInputData, + AudioBufferList* outOutputData) + { + int i; + const ScopedLock sl (callbackLock); + + if (callback != 0) + { + if (inputDevice == 0) + { + for (i = numInputChans; --i >= 0;) + { + const CallbackDetailsForChannel& info = inputChannelInfo[i]; + float* dest = tempInputBuffers [i]; + const float* src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + + info.dataOffsetSamples; + const int stride = info.dataStrideSamples; + + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) + { + *dest++ = *src; + src += stride; + } + } + } + } + + if (! isSlaveDevice) + { + if (inputDevice == 0) + { + callback->audioDeviceIOCallback ((const float**) tempInputBuffers, + numInputChans, + tempOutputBuffers, + numOutputChans, + bufferSize); + } + else + { + jassert (inputDevice->bufferSize == bufferSize); + + // Sometimes the two linked devices seem to get their callbacks in + // parallel, so we need to lock both devices to stop the input data being + // changed while inside our callback.. + const ScopedLock sl (inputDevice->callbackLock); + + callback->audioDeviceIOCallback ((const float**) inputDevice->tempInputBuffers, + inputDevice->numInputChans, + tempOutputBuffers, + numOutputChans, + bufferSize); + } + + for (i = numOutputChans; --i >= 0;) + { + const CallbackDetailsForChannel& info = outputChannelInfo[i]; + const float* src = tempOutputBuffers [i]; + float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + + info.dataOffsetSamples; + const int stride = info.dataStrideSamples; + + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) + { + *dest = *src++; + dest += stride; + } + } + } + } + } + else + { + for (i = jmin (numOutputChans, numOutputChannelInfos); --i >= 0;) + { + const CallbackDetailsForChannel& info = outputChannelInfo[i]; + float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + + info.dataOffsetSamples; + const int stride = info.dataStrideSamples; + + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) + { + *dest = 0.0f; + dest += stride; + } + } + } + } + } + + // called by callbacks + void deviceDetailsChanged() + { + if (callbacksAllowed) + startTimer (100); + } + + void timerCallback() + { + stopTimer(); + log ("CoreAudio device changed callback"); + + const double oldSampleRate = sampleRate; + const int oldBufferSize = bufferSize; + updateDetailsFromDevice(); + + if (oldBufferSize != bufferSize || oldSampleRate != sampleRate) + { + callbacksAllowed = false; + stop (false); + updateDetailsFromDevice(); + callbacksAllowed = true; + } + } + + CoreAudioInternal* getRelatedDevice() const + { + UInt32 size = 0; + CoreAudioInternal* result = 0; + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyRelatedDevices; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; + + if (deviceID != 0 + && AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size) == noErr + && size > 0) + { + AudioDeviceID* devs = (AudioDeviceID*) juce_calloc (size); + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, devs))) + { + for (unsigned int i = 0; i < size / sizeof (AudioDeviceID); ++i) + { + if (devs[i] != deviceID && devs[i] != 0) + { + result = new CoreAudioInternal (devs[i]); + + const bool thisIsInput = inChanNames.size() > 0 && outChanNames.size() == 0; + const bool otherIsInput = result->inChanNames.size() > 0 && result->outChanNames.size() == 0; + + if (thisIsInput != otherIsInput + || (inChanNames.size() + outChanNames.size() == 0) + || (result->inChanNames.size() + result->outChanNames.size()) == 0) + break; + + deleteAndZero (result); + } + } + } + + juce_free (devs); + } + + return result; + } + + juce_UseDebuggingNewOperator + + int inputLatency, outputLatency; + BitArray activeInputChans, activeOutputChans; + StringArray inChanNames, outChanNames; + Array sampleRates; + Array bufferSizes; + AudioIODeviceCallback* callback; +#if ! MACOS_10_4_OR_EARLIER + AudioDeviceIOProcID audioProcID; +#endif + + CoreAudioInternal* inputDevice; + bool isSlaveDevice; + +private: + CriticalSection callbackLock; + AudioDeviceID deviceID; + bool started; + double sampleRate; + int bufferSize; + float* audioBuffer; + int numInputChans, numOutputChans; + bool callbacksAllowed; + + struct CallbackDetailsForChannel + { + int streamNum; + int dataOffsetSamples; + int dataStrideSamples; + }; + + int numInputChannelInfos, numOutputChannelInfos; + CallbackDetailsForChannel* inputChannelInfo; + CallbackDetailsForChannel* outputChannelInfo; + float** tempInputBuffers; + float** tempOutputBuffers; + + CoreAudioInternal (const CoreAudioInternal&); + const CoreAudioInternal& operator= (const CoreAudioInternal&); + + static OSStatus audioIOProc (AudioDeviceID inDevice, + const AudioTimeStamp* inNow, + const AudioBufferList* inInputData, + const AudioTimeStamp* inInputTime, + AudioBufferList* outOutputData, + const AudioTimeStamp* inOutputTime, + void* device) + { + ((CoreAudioInternal*) device)->audioCallback (inInputData, outOutputData); + return noErr; + } + + static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) + { + CoreAudioInternal* const intern = (CoreAudioInternal*) inClientData; + + switch (pa->mSelector) + { + case kAudioDevicePropertyBufferSize: + case kAudioDevicePropertyBufferFrameSize: + case kAudioDevicePropertyNominalSampleRate: + case kAudioDevicePropertyStreamFormat: + case kAudioDevicePropertyDeviceIsAlive: + intern->deviceDetailsChanged(); + break; + + case kAudioDevicePropertyBufferSizeRange: + case kAudioDevicePropertyVolumeScalar: + case kAudioDevicePropertyMute: + case kAudioDevicePropertyPlayThru: + case kAudioDevicePropertyDataSource: + case kAudioDevicePropertyDeviceIsRunning: + break; + } + + return noErr; + } + + static OSType* getAllDataSourcesForDevice (AudioDeviceID deviceID, const bool input, int& num) + { + OSType* types = 0; + UInt32 size = 0; + num = 0; + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyDataSources; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; + + if (deviceID != 0 + && OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) + { + types = (OSType*) juce_calloc (size); + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, types))) + { + num = size / sizeof (OSType); + } + else + { + juce_free (types); + types = 0; + } + } + + return types; + } +}; + +class CoreAudioIODevice : public AudioIODevice +{ +public: + CoreAudioIODevice (const String& deviceName, + AudioDeviceID inputDeviceId, + const int inputIndex_, + AudioDeviceID outputDeviceId, + const int outputIndex_) + : AudioIODevice (deviceName, "CoreAudio"), + inputIndex (inputIndex_), + outputIndex (outputIndex_), + isOpen_ (false), + isStarted (false) + { + internal = 0; + CoreAudioInternal* device = 0; + + if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) + { + jassert (inputDeviceId != 0); + + device = new CoreAudioInternal (inputDeviceId); + } + else + { + device = new CoreAudioInternal (outputDeviceId); + + if (inputDeviceId != 0) + { + CoreAudioInternal* secondDevice = new CoreAudioInternal (inputDeviceId); + + device->inputDevice = secondDevice; + secondDevice->isSlaveDevice = true; + } + } + + internal = device; + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioObjectPropertySelectorWildcard; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; + + AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal); + } + + ~CoreAudioIODevice() + { + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioObjectPropertySelectorWildcard; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; + + AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal); + + delete internal; + } + + const StringArray getOutputChannelNames() + { + return internal->outChanNames; + } + + const StringArray getInputChannelNames() + { + if (internal->inputDevice != 0) + return internal->inputDevice->inChanNames; + else + return internal->inChanNames; + } + + int getNumSampleRates() + { + return internal->sampleRates.size(); + } + + double getSampleRate (int index) + { + return internal->sampleRates [index]; + } + + int getNumBufferSizesAvailable() + { + return internal->bufferSizes.size(); + } + + int getBufferSizeSamples (int index) + { + return internal->bufferSizes [index]; + } + + int getDefaultBufferSize() + { + for (int i = 0; i < getNumBufferSizesAvailable(); ++i) + if (getBufferSizeSamples(i) >= 512) + return getBufferSizeSamples(i); + + return 512; + } + + const String open (const BitArray& inputChannels, + const BitArray& outputChannels, + double sampleRate, + int bufferSizeSamples) + { + isOpen_ = true; + + if (bufferSizeSamples <= 0) + bufferSizeSamples = getDefaultBufferSize(); + + lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples); + isOpen_ = lastError.isEmpty(); + return lastError; + } + + void close() + { + isOpen_ = false; + internal->stop (false); + } + + bool isOpen() + { + return isOpen_; + } + + int getCurrentBufferSizeSamples() + { + return internal != 0 ? internal->getBufferSize() : 512; + } + + double getCurrentSampleRate() + { + return internal != 0 ? internal->getSampleRate() : 0; + } + + int getCurrentBitDepth() + { + return 32; // no way to find out, so just assume it's high.. + } + + const BitArray getActiveOutputChannels() const + { + return internal != 0 ? internal->activeOutputChans : BitArray(); + } + + const BitArray getActiveInputChannels() const + { + BitArray chans; + + if (internal != 0) + { + chans = internal->activeInputChans; + + if (internal->inputDevice != 0) + chans.orWith (internal->inputDevice->activeInputChans); + } + + return chans; + } + + int getOutputLatencyInSamples() + { + if (internal == 0) + return 0; + + // this seems like a good guess at getting the latency right - comparing + // this with a round-trip measurement, it gets it to within a few millisecs + // for the built-in mac soundcard + return internal->outputLatency + internal->getBufferSize() * 2; + } + + int getInputLatencyInSamples() + { + if (internal == 0) + return 0; + + return internal->inputLatency + internal->getBufferSize() * 2; + } + + void start (AudioIODeviceCallback* callback) + { + if (internal != 0 && ! isStarted) + { + if (callback != 0) + callback->audioDeviceAboutToStart (this); + + isStarted = true; + internal->start (callback); + } + } + + void stop() + { + if (isStarted && internal != 0) + { + AudioIODeviceCallback* const lastCallback = internal->callback; + + isStarted = false; + internal->stop (true); + + if (lastCallback != 0) + lastCallback->audioDeviceStopped(); + } + } + + bool isPlaying() + { + if (internal->callback == 0) + isStarted = false; + + return isStarted; + } + + const String getLastError() + { + return lastError; + } + + int inputIndex, outputIndex; + + juce_UseDebuggingNewOperator + +private: + CoreAudioInternal* internal; + bool isOpen_, isStarted; + String lastError; + + static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) + { + CoreAudioInternal* const intern = (CoreAudioInternal*) inClientData; + + switch (pa->mSelector) + { + case kAudioHardwarePropertyDevices: + intern->deviceDetailsChanged(); + break; + + case kAudioHardwarePropertyDefaultOutputDevice: + case kAudioHardwarePropertyDefaultInputDevice: + case kAudioHardwarePropertyDefaultSystemOutputDevice: + break; + } + + return noErr; + } + + CoreAudioIODevice (const CoreAudioIODevice&); + const CoreAudioIODevice& operator= (const CoreAudioIODevice&); +}; + +class CoreAudioIODeviceType : public AudioIODeviceType +{ +public: + + CoreAudioIODeviceType() + : AudioIODeviceType (T("CoreAudio")), + hasScanned (false) + { + } + + ~CoreAudioIODeviceType() + { + } + + void scanForDevices() + { + hasScanned = true; + + inputDeviceNames.clear(); + outputDeviceNames.clear(); + inputIds.clear(); + outputIds.clear(); + + UInt32 size; + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioHardwarePropertyDevices; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; + + if (OK (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, 0, &size))) + { + AudioDeviceID* const devs = (AudioDeviceID*) juce_calloc (size); + + if (OK (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, 0, &size, devs))) + { + static bool alreadyLogged = false; + const int num = size / sizeof (AudioDeviceID); + for (int i = 0; i < num; ++i) + { + char name [1024]; + size = sizeof (name); + pa.mSelector = kAudioDevicePropertyDeviceName; + + if (OK (AudioObjectGetPropertyData (devs[i], &pa, 0, 0, &size, name))) + { + const String nameString (String::fromUTF8 ((const uint8*) name, strlen (name))); + + if (! alreadyLogged) + log (T("CoreAudio device: ") + nameString); + + const int numIns = getNumChannels (devs[i], true); + const int numOuts = getNumChannels (devs[i], false); + + if (numIns > 0) + { + inputDeviceNames.add (nameString); + inputIds.add (devs[i]); + } + + if (numOuts > 0) + { + outputDeviceNames.add (nameString); + outputIds.add (devs[i]); + } + } + } + + alreadyLogged = true; + } + + juce_free (devs); + } + + inputDeviceNames.appendNumbersToDuplicates (false, true); + outputDeviceNames.appendNumbersToDuplicates (false, true); + } + + const StringArray getDeviceNames (const bool wantInputNames) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + if (wantInputNames) + return inputDeviceNames; + else + return outputDeviceNames; + } + + int getDefaultDeviceIndex (const bool forInput) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + AudioDeviceID deviceID; + UInt32 size = sizeof (deviceID); + + // if they're asking for any input channels at all, use the default input, so we + // get the built-in mic rather than the built-in output with no inputs.. + + AudioObjectPropertyAddress pa; + pa.mSelector = forInput ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; + + if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, 0, &size, &deviceID) == noErr) + { + if (forInput) + { + for (int i = inputIds.size(); --i >= 0;) + if (inputIds[i] == deviceID) + return i; + } + else + { + for (int i = outputIds.size(); --i >= 0;) + if (outputIds[i] == deviceID) + return i; + } + } + + return 0; + } + + int getIndexOfDevice (AudioIODevice* device, const bool asInput) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + CoreAudioIODevice* const d = dynamic_cast (device); + if (d == 0) + return -1; + + return asInput ? d->inputIndex + : d->outputIndex; + } + + bool hasSeparateInputsAndOutputs() const { return true; } + + AudioIODevice* createDevice (const String& outputDeviceName, + const String& inputDeviceName) + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + const int inputIndex = inputDeviceNames.indexOf (inputDeviceName); + const int outputIndex = outputDeviceNames.indexOf (outputDeviceName); + + String deviceName (outputDeviceName); + if (deviceName.isEmpty()) + deviceName = inputDeviceName; + + if (index >= 0) + return new CoreAudioIODevice (deviceName, + inputIds [inputIndex], + inputIndex, + outputIds [outputIndex], + outputIndex); + + return 0; + } + + juce_UseDebuggingNewOperator + +private: + StringArray inputDeviceNames, outputDeviceNames; + Array inputIds, outputIds; + + bool hasScanned; + + static int getNumChannels (AudioDeviceID deviceID, bool input) + { + int total = 0; + UInt32 size; + + AudioObjectPropertyAddress pa; + pa.mSelector = kAudioDevicePropertyStreamConfiguration; + pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + pa.mElement = kAudioObjectPropertyElementMaster; + + if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) + { + AudioBufferList* const bufList = (AudioBufferList*) juce_calloc (size); + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, bufList))) + { + const int numStreams = bufList->mNumberBuffers; + + for (int i = 0; i < numStreams; ++i) + { + const AudioBuffer& b = bufList->mBuffers[i]; + total += b.mNumberChannels; + } + } + + juce_free (bufList); + } + + return total; + } + + CoreAudioIODeviceType (const CoreAudioIODeviceType&); + const CoreAudioIODeviceType& operator= (const CoreAudioIODeviceType&); +}; + +AudioIODeviceType* juce_createAudioIODeviceType_CoreAudio() +{ + return new CoreAudioIODeviceType(); +} + +#undef log + +#endif +/********* End of inlined file: juce_mac_CoreAudio.cpp *********/ + +/********* Start of inlined file: juce_mac_CoreMidi.cpp *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +#undef log +#define log(a) Logger::writeToLog(a) + +static bool logAnyErrorsMidi (const OSStatus err, const int lineNum) +{ + if (err == noErr) + return true; + + log (T("CoreMidi error: ") + String (lineNum) + T(" - ") + String::toHexString ((int)err)); + jassertfalse + return false; +} + +#undef OK +#define OK(a) logAnyErrorsMidi(a, __LINE__) + +static const String getEndpointName (MIDIEndpointRef endpoint, bool isExternal) +{ + String result; + CFStringRef str = 0; + + MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &str); + + if (str != 0) + { + result = PlatformUtilities::cfStringToJuceString (str); + CFRelease (str); + str = 0; + } + + MIDIEntityRef entity = 0; + MIDIEndpointGetEntity (endpoint, &entity); + + if (entity == 0) + return result; // probably virtual + + if (result.isEmpty()) + { + // endpoint name has zero length - try the entity + MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str); + + if (str != 0) + { + result += PlatformUtilities::cfStringToJuceString (str); + CFRelease (str); + str = 0; + } + } + + // now consider the device's name + MIDIDeviceRef device = 0; + MIDIEntityGetDevice (entity, &device); + if (device == 0) + return result; + + MIDIObjectGetStringProperty (device, kMIDIPropertyName, &str); + + if (str != 0) + { + const String s (PlatformUtilities::cfStringToJuceString (str)); + CFRelease (str); + + // if an external device has only one entity, throw away + // the endpoint name and just use the device name + if (isExternal && MIDIDeviceGetNumberOfEntities (device) < 2) + { + result = s; + } + else if (! result.startsWithIgnoreCase (s)) + { + // prepend the device name to the entity name + result = (s + T(" ") + result).trimEnd(); + } + } + + return result; +} + +static const String getConnectedEndpointName (MIDIEndpointRef endpoint) +{ + String result; + + // Does the endpoint have connections? + CFDataRef connections = 0; + int numConnections = 0; + + MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections); + + if (connections != 0) + { + numConnections = CFDataGetLength (connections) / sizeof (MIDIUniqueID); + + if (numConnections > 0) + { + const SInt32* pid = reinterpret_cast (CFDataGetBytePtr (connections)); + + for (int i = 0; i < numConnections; ++i, ++pid) + { + MIDIUniqueID uid = EndianS32_BtoN (*pid); + MIDIObjectRef connObject; + MIDIObjectType connObjectType; + OSStatus err = MIDIObjectFindByUniqueID (uid, &connObject, &connObjectType); + + if (err == noErr) + { + String s; + + if (connObjectType == kMIDIObjectType_ExternalSource + || connObjectType == kMIDIObjectType_ExternalDestination) + { + // Connected to an external device's endpoint (10.3 and later). + s = getEndpointName (static_cast (connObject), true); + } + else + { + // Connected to an external device (10.2) (or something else, catch-all) + CFStringRef str = 0; + MIDIObjectGetStringProperty (connObject, kMIDIPropertyName, &str); + + if (str != 0) + { + s = PlatformUtilities::cfStringToJuceString (str); + CFRelease (str); + } + } + + if (s.isNotEmpty()) + { + if (result.isNotEmpty()) + result += (", "); + + result += s; + } + } + } + } + + CFRelease (connections); + } + + if (result.isNotEmpty()) + return result; + + // Here, either the endpoint had no connections, or we failed to obtain names for any of them. + return getEndpointName (endpoint, false); +} + +const StringArray MidiOutput::getDevices() +{ + StringArray s; + + const ItemCount num = MIDIGetNumberOfDestinations(); + for (ItemCount i = 0; i < num; ++i) + { + MIDIEndpointRef dest = MIDIGetDestination (i); + + if (dest != 0) + { + String name (getConnectedEndpointName (dest)); + + if (name.isEmpty()) + name = ""; + + s.add (name); + } + else + { + s.add (""); + } + } + + return s; +} + +int MidiOutput::getDefaultDeviceIndex() +{ + return 0; +} + +static MIDIClientRef globalMidiClient; +static bool hasGlobalClientBeenCreated = false; + +static bool makeSureClientExists() +{ + if (! hasGlobalClientBeenCreated) + { + String name (T("JUCE")); + + if (JUCEApplication::getInstance() != 0) + name = JUCEApplication::getInstance()->getApplicationName(); + + CFStringRef appName = PlatformUtilities::juceStringToCFString (name); + + hasGlobalClientBeenCreated = OK (MIDIClientCreate (appName, 0, 0, &globalMidiClient)); + CFRelease (appName); + } + + return hasGlobalClientBeenCreated; +} + +struct MidiPortAndEndpoint +{ + MIDIPortRef port; + MIDIEndpointRef endPoint; +}; + +MidiOutput* MidiOutput::openDevice (int index) +{ + MidiOutput* mo = 0; + + if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfDestinations()) + { + MIDIEndpointRef endPoint = MIDIGetDestination (index); + + CFStringRef pname; + if (OK (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) + { + log (T("CoreMidi - opening out: ") + PlatformUtilities::cfStringToJuceString (pname)); + + if (makeSureClientExists()) + { + MIDIPortRef port; + + if (OK (MIDIOutputPortCreate (globalMidiClient, pname, &port))) + { + MidiPortAndEndpoint* mpe = new MidiPortAndEndpoint(); + mpe->port = port; + mpe->endPoint = endPoint; + + mo = new MidiOutput(); + mo->internal = (void*)mpe; + } + } + + CFRelease (pname); + } + } + + return mo; +} + +MidiOutput::~MidiOutput() +{ + MidiPortAndEndpoint* const mpe = (MidiPortAndEndpoint*)internal; + MIDIPortDispose (mpe->port); + delete mpe; +} + +void MidiOutput::reset() +{ +} + +bool MidiOutput::getVolume (float& leftVol, float& rightVol) +{ + return false; +} + +void MidiOutput::setVolume (float leftVol, float rightVol) +{ +} + +void MidiOutput::sendMessageNow (const MidiMessage& message) +{ + MidiPortAndEndpoint* const mpe = (MidiPortAndEndpoint*)internal; + + if (message.isSysEx()) + { + const int maxPacketSize = 256; + int pos = 0, bytesLeft = message.getRawDataSize(); + const int numPackets = (bytesLeft + maxPacketSize - 1) / maxPacketSize; + MIDIPacketList* const packets = (MIDIPacketList*) juce_malloc (32 * numPackets + message.getRawDataSize()); + packets->numPackets = numPackets; + + MIDIPacket* p = packets->packet; + + for (int i = 0; i < numPackets; ++i) + { + p->timeStamp = 0; + p->length = jmin (maxPacketSize, bytesLeft); + memcpy (p->data, message.getRawData() + pos, p->length); + pos += p->length; + bytesLeft -= p->length; + p = MIDIPacketNext (p); + } + + MIDISend (mpe->port, mpe->endPoint, packets); + juce_free (packets); + } + else + { + MIDIPacketList packets; + packets.numPackets = 1; + packets.packet[0].timeStamp = 0; + packets.packet[0].length = message.getRawDataSize(); + *(int*) (packets.packet[0].data) = *(const int*) message.getRawData(); + + MIDISend (mpe->port, mpe->endPoint, &packets); + } +} + +const StringArray MidiInput::getDevices() +{ + StringArray s; + + const ItemCount num = MIDIGetNumberOfSources(); + for (ItemCount i = 0; i < num; ++i) + { + MIDIEndpointRef source = MIDIGetSource (i); + + if (source != 0) + { + String name (getConnectedEndpointName (source)); + + if (name.isEmpty()) + name = ""; + + s.add (name); + } + else + { + s.add (""); + } + } + + return s; +} + +int MidiInput::getDefaultDeviceIndex() +{ + return 0; +} + +struct MidiPortAndCallback +{ + MidiInput* input; + MIDIPortRef port; + MIDIEndpointRef endPoint; + MidiInputCallback* callback; + MemoryBlock pendingData; + int pendingBytes; + double pendingDataTime; + bool active; +}; + +static CriticalSection callbackLock; +static VoidArray activeCallbacks; + +static void processSysex (MidiPortAndCallback* const mpe, const uint8*& d, int& size, const double time) +{ + if (*d == 0xf0) + { + mpe->pendingBytes = 0; + mpe->pendingDataTime = time; + } + + mpe->pendingData.ensureSize (mpe->pendingBytes + size, false); + uint8* totalMessage = (uint8*) mpe->pendingData.getData(); + + uint8* dest = totalMessage + mpe->pendingBytes; + + while (size > 0) + { + if (mpe->pendingBytes > 0 && *d >= 0x80) + { + if (*d >= 0xfa || *d == 0xf8) + { + mpe->callback->handleIncomingMidiMessage (mpe->input, MidiMessage (*d, time)); + ++d; + --size; + } + else + { + if (*d == 0xf7) + { + *dest++ = *d++; + mpe->pendingBytes++; + --size; + } + + break; + } + } + else + { + *dest++ = *d++; + mpe->pendingBytes++; + --size; + } + } + + if (totalMessage [mpe->pendingBytes - 1] == 0xf7) + { + mpe->callback->handleIncomingMidiMessage (mpe->input, MidiMessage (totalMessage, + mpe->pendingBytes, + mpe->pendingDataTime)); + mpe->pendingBytes = 0; + } + else + { + mpe->callback->handlePartialSysexMessage (mpe->input, + totalMessage, + mpe->pendingBytes, + mpe->pendingDataTime); + } +} + +static void midiInputProc (const MIDIPacketList* pktlist, + void* readProcRefCon, + void* srcConnRefCon) +{ + double time = Time::getMillisecondCounterHiRes() * 0.001; + const double originalTime = time; + + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) readProcRefCon; + const ScopedLock sl (callbackLock); + + if (activeCallbacks.contains (mpe) && mpe->active) + { + const MIDIPacket* packet = &pktlist->packet[0]; + + for (unsigned int i = 0; i < pktlist->numPackets; ++i) + { + const uint8* d = (const uint8*) (packet->data); + int size = packet->length; + + while (size > 0) + { + time = originalTime; + + if (mpe->pendingBytes > 0 || d[0] == 0xf0) + { + processSysex (mpe, d, size, time); + } + else + { + int used = 0; + const MidiMessage m (d, size, used, 0, time); + + if (used <= 0) + { + jassertfalse // malformed midi message + break; + } + else + { + mpe->callback->handleIncomingMidiMessage (mpe->input, m); + } + + size -= used; + d += used; + } + } + + packet = MIDIPacketNext (packet); + } + } +} + +MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) +{ + MidiInput* mi = 0; + + if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfSources()) + { + MIDIEndpointRef endPoint = MIDIGetSource (index); + + if (endPoint != 0) + { + CFStringRef pname; + + if (OK (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) + { + log (T("CoreMidi - opening inp: ") + PlatformUtilities::cfStringToJuceString (pname)); + + if (makeSureClientExists()) + { + MIDIPortRef port; + + MidiPortAndCallback* const mpe = new MidiPortAndCallback(); + mpe->active = false; + + if (OK (MIDIInputPortCreate (globalMidiClient, pname, midiInputProc, mpe, &port))) + { + if (OK (MIDIPortConnectSource (port, endPoint, 0))) + { + mpe->port = port; + mpe->endPoint = endPoint; + mpe->callback = callback; + mpe->pendingBytes = 0; + mpe->pendingData.ensureSize (128); + + mi = new MidiInput (getDevices() [index]); + mpe->input = mi; + mi->internal = (void*) mpe; + + const ScopedLock sl (callbackLock); + activeCallbacks.add (mpe); + } + else + { + OK (MIDIPortDispose (port)); + delete mpe; + } + } + else + { + delete mpe; + } + } + } + + CFRelease (pname); + } + } + + return mi; +} + +MidiInput::MidiInput (const String& name_) + : name (name_) +{ +} + +MidiInput::~MidiInput() +{ + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; + mpe->active = false; + + callbackLock.enter(); + activeCallbacks.removeValue (mpe); + callbackLock.exit(); + + OK (MIDIPortDisconnectSource (mpe->port, mpe->endPoint)); + OK (MIDIPortDispose (mpe->port)); + delete mpe; +} + +void MidiInput::start() +{ + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; + const ScopedLock sl (callbackLock); + mpe->active = true; +} + +void MidiInput::stop() +{ + MidiPortAndCallback* const mpe = (MidiPortAndCallback*) internal; + const ScopedLock sl (callbackLock); + mpe->active = false; +} + +#undef log + +#endif +/********* End of inlined file: juce_mac_CoreMidi.cpp *********/ + +/********* Start of inlined file: juce_mac_CameraDevice.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE && JUCE_QUICKTIME && JUCE_USE_CAMERA + +#define QTCaptureCallbackDelegate MakeObjCClassName(QTCaptureCallbackDelegate) + +class QTCameraDeviceInteral; + +END_JUCE_NAMESPACE + +@interface QTCaptureCallbackDelegate : NSObject +{ +@public + CameraDevice* owner; + QTCameraDeviceInteral* internal; + Time* firstRecordedTime; +} + +- (QTCaptureCallbackDelegate*) initWithOwner: (CameraDevice*) owner internalDev: (QTCameraDeviceInteral*) d; +- (void) dealloc; + +- (void) captureOutput: (QTCaptureOutput*) captureOutput + didOutputVideoFrame: (CVImageBufferRef) videoFrame + withSampleBuffer: (QTSampleBuffer*) sampleBuffer + fromConnection: (QTCaptureConnection*) connection; + +- (void) captureOutput: (QTCaptureFileOutput*) captureOutput + didOutputSampleBuffer: (QTSampleBuffer*) sampleBuffer + fromConnection: (QTCaptureConnection*) connection; + +@end + +BEGIN_JUCE_NAMESPACE + +class QTCameraDeviceInteral +{ +public: + QTCameraDeviceInteral (CameraDevice* owner, int index) + { + const ScopedAutoReleasePool pool; + + session = [[QTCaptureSession alloc] init]; + + NSArray* devs = [QTCaptureDevice inputDevicesWithMediaType: QTMediaTypeVideo]; + device = (QTCaptureDevice*) [devs objectAtIndex: index]; + input = 0; + fileOutput = 0; + imageOutput = 0; + callbackDelegate = [[QTCaptureCallbackDelegate alloc] initWithOwner: owner + internalDev: this]; + + NSError* err = 0; + [device retain]; + [device open: &err]; + + if (err == 0) + { + input = [[QTCaptureDeviceInput alloc] initWithDevice: device]; + + [session addInput: input error: &err]; + + if (err == 0) + { + resetFile(); + + imageOutput = [[QTCaptureDecompressedVideoOutput alloc] init]; + [imageOutput setDelegate: callbackDelegate]; + + if (err == 0) + { + [session startRunning]; + return; + } + } + } + + openingError = nsStringToJuce ([err description]); + DBG (openingError); + } + + ~QTCameraDeviceInteral() + { + [session stopRunning]; + [session removeOutput: imageOutput]; + + [session release]; + [input release]; + [device release]; + [fileOutput release]; + [imageOutput release]; + [callbackDelegate release]; + } + + void resetFile() + { + [session removeOutput: fileOutput]; + [fileOutput release]; + fileOutput = [[QTCaptureMovieFileOutput alloc] init]; + [fileOutput setDelegate: callbackDelegate]; + } + + void addListener (CameraImageListener* listenerToAdd) + { + const ScopedLock sl (listenerLock); + + if (listeners.size() == 0) + [session addOutput: imageOutput error: nil]; + + listeners.addIfNotAlreadyThere (listenerToAdd); + } + + void removeListener (CameraImageListener* listenerToRemove) + { + const ScopedLock sl (listenerLock); + listeners.removeValue (listenerToRemove); + + if (listeners.size() == 0) + [session removeOutput: imageOutput]; + } + + static void drawNSBitmapIntoJuceImage (Image& dest, NSBitmapImageRep* source) + { + const ScopedAutoReleasePool pool; + int lineStride, pixelStride; + uint8* pixels = dest.lockPixelDataReadWrite (0, 0, dest.getWidth(), dest.getHeight(), + lineStride, pixelStride); + + NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes: &pixels + pixelsWide: dest.getWidth() + pixelsHigh: dest.getHeight() + bitsPerSample: 8 + samplesPerPixel: pixelStride + hasAlpha: dest.hasAlphaChannel() + isPlanar: NO + colorSpaceName: NSCalibratedRGBColorSpace + bitmapFormat: (NSBitmapFormat) 0 + bytesPerRow: lineStride + bitsPerPixel: pixelStride * 8]; + + [NSGraphicsContext saveGraphicsState]; + [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithBitmapImageRep: rep]]; + + [source drawAtPoint: NSZeroPoint]; + + [[NSGraphicsContext currentContext] flushGraphics]; + [NSGraphicsContext restoreGraphicsState]; + + uint8* start = pixels; + for (int h = dest.getHeight(); --h >= 0;) + { + uint8* p = start; + start += lineStride; + + for (int i = dest.getWidth(); --i >= 0;) + { +#if JUCE_BIG_ENDIAN + const uint8 oldp3 = p[3]; + const uint8 oldp1 = p[1]; + p[3] = p[0]; + p[0] = oldp1; + p[1] = p[2]; + p[2] = oldp3; +#else + const uint8 oldp0 = p[0]; + p[0] = p[2]; + p[2] = oldp0; +#endif + + p += pixelStride; + } + } + + dest.releasePixelDataReadWrite (pixels); + } + + void callListeners (NSBitmapImageRep* bitmap) + { + Image image (Image::ARGB, [bitmap size].width, [bitmap size].height, false); + drawNSBitmapIntoJuceImage (image, bitmap); + + const ScopedLock sl (listenerLock); + + for (int i = listeners.size(); --i >= 0;) + { + CameraImageListener* l = (CameraImageListener*) listeners[i]; + + if (l != 0) + l->imageReceived (image); + } + } + + QTCaptureDevice* device; + QTCaptureDeviceInput* input; + QTCaptureSession* session; + QTCaptureMovieFileOutput* fileOutput; + QTCaptureDecompressedVideoOutput* imageOutput; + QTCaptureCallbackDelegate* callbackDelegate; + String openingError; + + VoidArray listeners; + CriticalSection listenerLock; +}; + +END_JUCE_NAMESPACE +@implementation QTCaptureCallbackDelegate + +- (QTCaptureCallbackDelegate*) initWithOwner: (CameraDevice*) owner_ + internalDev: (QTCameraDeviceInteral*) d +{ + [super init]; + owner = owner_; + internal = d; + firstRecordedTime = 0; + return self; +} + +- (void) dealloc +{ + delete firstRecordedTime; + [super dealloc]; +} + +- (void) captureOutput: (QTCaptureOutput*) captureOutput + didOutputVideoFrame: (CVImageBufferRef) videoFrame + withSampleBuffer: (QTSampleBuffer*) sampleBuffer + fromConnection: (QTCaptureConnection*) connection +{ + const ScopedAutoReleasePool pool; + CIImage* image = [CIImage imageWithCVImageBuffer: videoFrame]; + NSBitmapImageRep* bitmap = [[[NSBitmapImageRep alloc] initWithCIImage: image] autorelease]; + + internal->callListeners (bitmap); +} + +- (void) captureOutput: (QTCaptureFileOutput*) captureOutput + didOutputSampleBuffer: (QTSampleBuffer*) sampleBuffer + fromConnection: (QTCaptureConnection*) connection +{ + if (firstRecordedTime == 0) + firstRecordedTime = new Time (Time::getCurrentTime()); +} + +@end +BEGIN_JUCE_NAMESPACE + +class QTCaptureViewerComp : public NSViewComponent +{ +public: + QTCaptureViewerComp (CameraDevice* const cameraDevice, QTCameraDeviceInteral* const internal) + { + const ScopedAutoReleasePool pool; + captureView = [[QTCaptureView alloc] init]; + [captureView setCaptureSession: internal->session]; + + setSize (640, 480); // xxx need to somehow get the movie size - how? + setView (captureView); + } + + ~QTCaptureViewerComp() + { + setView (0); + [captureView setCaptureSession: nil]; + [captureView release]; + } + + QTCaptureView* captureView; +}; + +CameraDevice::CameraDevice (const String& name_, int index) + : name (name_) +{ + isRecording = false; + QTCameraDeviceInteral* d = new QTCameraDeviceInteral (this, index); + internal = d; +} + +CameraDevice::~CameraDevice() +{ + stopRecording(); + delete (QTCameraDeviceInteral*) internal; + internal = 0; +} + +Component* CameraDevice::createViewerComponent() +{ + return new QTCaptureViewerComp (this, (QTCameraDeviceInteral*) internal); +} + +const String CameraDevice::getFileExtension() +{ + return ".mov"; +} + +void CameraDevice::startRecordingToFile (const File& file) +{ + stopRecording(); + + QTCameraDeviceInteral* const d = (QTCameraDeviceInteral*) internal; + deleteAndZero (d->callbackDelegate->firstRecordedTime); + file.deleteFile(); + [d->fileOutput recordToOutputFileURL: [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())]]; + [d->session addOutput: d->fileOutput error: nil]; + isRecording = true; +} + +const Time CameraDevice::getTimeOfFirstRecordedFrame() const +{ + QTCameraDeviceInteral* const d = (QTCameraDeviceInteral*) internal; + if (d->callbackDelegate->firstRecordedTime != 0) + return *d->callbackDelegate->firstRecordedTime; + + return Time(); +} + +void CameraDevice::stopRecording() +{ + if (isRecording) + { + QTCameraDeviceInteral* const d = (QTCameraDeviceInteral*) internal; + d->resetFile(); + isRecording = false; + } +} + +void CameraDevice::addListener (CameraImageListener* listenerToAdd) +{ + QTCameraDeviceInteral* const d = (QTCameraDeviceInteral*) internal; + + if (listenerToAdd != 0) + d->addListener (listenerToAdd); +} + +void CameraDevice::removeListener (CameraImageListener* listenerToRemove) +{ + QTCameraDeviceInteral* const d = (QTCameraDeviceInteral*) internal; + + if (listenerToRemove != 0) + d->removeListener (listenerToRemove); +} + +const StringArray CameraDevice::getAvailableDevices() +{ + const ScopedAutoReleasePool pool; + + StringArray results; + NSArray* devs = [QTCaptureDevice inputDevicesWithMediaType: QTMediaTypeVideo]; + + for (int i = 0; i < [devs count]; ++i) + { + QTCaptureDevice* dev = (QTCaptureDevice*) [devs objectAtIndex: i]; + results.add (nsStringToJuce ([dev localizedDisplayName])); + } + + return results; +} + +CameraDevice* CameraDevice::openDevice (int index, + int minWidth, int minHeight, + int maxWidth, int maxHeight) +{ + CameraDevice* d = new CameraDevice (getAvailableDevices() [index], index); + + if (((QTCameraDeviceInteral*) (d->internal))->openingError.isEmpty()) + return d; + + delete d; + return 0; +} + +#endif +/********* End of inlined file: juce_mac_CameraDevice.mm *********/ + +*/ +#endif + +END_JUCE_NAMESPACE +/********* End of inlined file: juce_iphone_NativeCode.mm *********/ + +#endif diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 40a2706728..5600a8ab5f 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -145,7 +145,7 @@ On Windows, if you enable this, you'll need to have the QuickTime SDK installed, and its header files will need to be on your include path. */ -#if ! (defined (JUCE_QUICKTIME) || defined (LINUX) || (defined (_WIN32) && ! defined (_MSC_VER))) +#if ! (defined (JUCE_QUICKTIME) || defined (LINUX) || defined (TARGET_OS_IPHONE) || defined (TARGET_IPHONE_SIMULATOR) || (defined (_WIN32) && ! defined (_MSC_VER))) #define JUCE_QUICKTIME 1 #endif @@ -326,6 +326,8 @@ #else #if defined (LINUX) || defined (__linux__) #define JUCE_LINUX 1 + #elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + #define JUCE_IPHONE 1 #else #define JUCE_MAC 1 #endif @@ -389,6 +391,20 @@ #endif #endif +#if JUCE_IPHONE + #include + + #ifndef NDEBUG + #define JUCE_DEBUG 1 + #endif + + #ifdef __LITTLE_ENDIAN__ + #define JUCE_LITTLE_ENDIAN 1 + #else + #define JUCE_BIG_ENDIAN 1 + #endif +#endif + #if JUCE_LINUX #ifdef _DEBUG @@ -494,6 +510,8 @@ #endif #elif JUCE_MAC #define juce_breakDebugger Debugger(); + #elif JUCE_IPHONE + #define juce_breakDebugger assert (false); #elif JUCE_LINUX #define juce_breakDebugger kill (0, SIGTRAP); #endif @@ -1079,7 +1097,7 @@ const float float_Pi = 3.14159265358979323846f; /** Swaps the byte-order in an integer from little to big-endianness or vice-versa. */ forcedinline uint32 swapByteOrder (uint32 n) throw() { -#if JUCE_MAC +#if JUCE_MAC || JUCE_IPHONE // Mac version return CFSwapInt32 (n); #elif JUCE_GCC @@ -1113,7 +1131,7 @@ inline uint16 swapByteOrder (const uint16 n) throw() inline uint64 swapByteOrder (const uint64 value) throw() { -#if JUCE_MAC +#if JUCE_MAC || JUCE_IPHONE return CFSwapInt64 (value); #elif JUCE_USE_INTRINSICS return _byteswap_uint64 (value); @@ -2536,7 +2554,7 @@ BEGIN_JUCE_NAMESPACE // Atomic increment/decrement operations.. -#if JUCE_MAC && ! DOXYGEN +#if (JUCE_MAC || JUCE_IPHONE) && ! DOXYGEN #if ! MACOS_10_3_OR_EARLIER @@ -7147,7 +7165,7 @@ public: const String& bodyText, const StringArray& filesToAttach); -#if JUCE_MAC || DOXYGEN +#if JUCE_MAC || JUCE_IPHONE || DOXYGEN /** MAC ONLY - Turns a Core CF String into a juce one. */ static const String cfStringToJuceString (CFStringRef cfString); @@ -7288,7 +7306,7 @@ public: #endif }; -#if JUCE_MAC +#if JUCE_MAC || JUCE_IPHONE /** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII. @@ -7303,6 +7321,10 @@ private: void* pool; }; +#endif + +#if JUCE_MAC + /** A wrapper class for picking up events from an Apple IR remote control device. @@ -8302,10 +8324,12 @@ public: @param addresses an array into which the MAC addresses should be copied @param maxNum the number of elements in this array - @param littleEndian the endianness of the numbers to return. Note that - the default values of this parameter are different on - Mac/PC to avoid breaking old software that was written - before this parameter was added (when the two systems + @param littleEndian the endianness of the numbers to return. If this is true, + the least-significant byte of each number is the first byte + of the mac address. If false, the least significant byte is + the last number. Note that the default values of this parameter + are different on Mac/PC to avoid breaking old software that was + written before this parameter was added (when the two systems defaulted to using different endiannesses). In newer software you probably want to specify an explicit value for this. diff --git a/src/core/juce_Atomic.h b/src/core/juce_Atomic.h index 63299af9be..00d0be9c4f 100644 --- a/src/core/juce_Atomic.h +++ b/src/core/juce_Atomic.h @@ -31,7 +31,7 @@ //============================================================================== -#if JUCE_MAC && ! DOXYGEN +#if (JUCE_MAC || JUCE_IPHONE) && ! DOXYGEN #if ! MACOS_10_3_OR_EARLIER //============================================================================== diff --git a/src/core/juce_DataConversions.h b/src/core/juce_DataConversions.h index 1478c94f50..877b8eb158 100644 --- a/src/core/juce_DataConversions.h +++ b/src/core/juce_DataConversions.h @@ -38,7 +38,7 @@ /** Swaps the byte-order in an integer from little to big-endianness or vice-versa. */ forcedinline uint32 swapByteOrder (uint32 n) throw() { -#if JUCE_MAC +#if JUCE_MAC || JUCE_IPHONE // Mac version return CFSwapInt32 (n); #elif JUCE_GCC @@ -72,7 +72,7 @@ inline uint16 swapByteOrder (const uint16 n) throw() inline uint64 swapByteOrder (const uint64 value) throw() { -#if JUCE_MAC +#if JUCE_MAC || JUCE_IPHONE return CFSwapInt64 (value); #elif JUCE_USE_INTRINSICS return _byteswap_uint64 (value); diff --git a/src/core/juce_PlatformDefs.h b/src/core/juce_PlatformDefs.h index 89f49504d2..32f1a9978a 100644 --- a/src/core/juce_PlatformDefs.h +++ b/src/core/juce_PlatformDefs.h @@ -48,6 +48,8 @@ #else #if defined (LINUX) || defined (__linux__) #define JUCE_LINUX 1 + #elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + #define JUCE_IPHONE 1 #else #define JUCE_MAC 1 #endif @@ -113,6 +115,21 @@ #endif #endif +//============================================================================== +#if JUCE_IPHONE + #include + + #ifndef NDEBUG + #define JUCE_DEBUG 1 + #endif + + #ifdef __LITTLE_ENDIAN__ + #define JUCE_LITTLE_ENDIAN 1 + #else + #define JUCE_BIG_ENDIAN 1 + #endif +#endif + //============================================================================== #if JUCE_LINUX @@ -223,6 +240,8 @@ #endif #elif JUCE_MAC #define juce_breakDebugger Debugger(); + #elif JUCE_IPHONE + #define juce_breakDebugger assert (false); #elif JUCE_LINUX #define juce_breakDebugger kill (0, SIGTRAP); #endif diff --git a/src/core/juce_PlatformUtilities.h b/src/core/juce_PlatformUtilities.h index f4c1099da4..d58763c44f 100644 --- a/src/core/juce_PlatformUtilities.h +++ b/src/core/juce_PlatformUtilities.h @@ -46,7 +46,7 @@ public: const String& bodyText, const StringArray& filesToAttach); -#if JUCE_MAC || DOXYGEN +#if JUCE_MAC || JUCE_IPHONE || DOXYGEN //============================================================================== /** MAC ONLY - Turns a Core CF String into a juce one. */ static const String cfStringToJuceString (CFStringRef cfString); @@ -191,7 +191,7 @@ public: }; -#if JUCE_MAC +#if JUCE_MAC || JUCE_IPHONE //============================================================================== /** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object @@ -207,6 +207,10 @@ private: void* pool; }; +#endif + +#if JUCE_MAC + //============================================================================== /** A wrapper class for picking up events from an Apple IR remote control device. diff --git a/src/core/juce_SystemStats.cpp b/src/core/juce_SystemStats.cpp index cf6e2af391..142674067d 100644 --- a/src/core/juce_SystemStats.cpp +++ b/src/core/juce_SystemStats.cpp @@ -78,10 +78,9 @@ void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI() juceInitialisedNonGUI = true; DBG (SystemStats::getJUCEVersion()); - Random::getSystemRandom().setSeedRandomly(); // (calling this more than once improves its randomness) juce_initialiseStrings(); SystemStats::initialiseStats(); - Random::getSystemRandom().setSeedRandomly(); // (calling this more than once improves its randomness) + Random::getSystemRandom().setSeedRandomly(); // (mustn't call this before initialiseStats() because it relies on the time being set up) } } diff --git a/src/core/juce_SystemStats.h b/src/core/juce_SystemStats.h index 456a7952af..29a6baa230 100644 --- a/src/core/juce_SystemStats.h +++ b/src/core/juce_SystemStats.h @@ -147,10 +147,12 @@ public: @param addresses an array into which the MAC addresses should be copied @param maxNum the number of elements in this array - @param littleEndian the endianness of the numbers to return. Note that - the default values of this parameter are different on - Mac/PC to avoid breaking old software that was written - before this parameter was added (when the two systems + @param littleEndian the endianness of the numbers to return. If this is true, + the least-significant byte of each number is the first byte + of the mac address. If false, the least significant byte is + the last number. Note that the default values of this parameter + are different on Mac/PC to avoid breaking old software that was + written before this parameter was added (when the two systems defaulted to using different endiannesses). In newer software you probably want to specify an explicit value for this. diff --git a/src/gui/components/filebrowser/juce_FileTreeComponent.cpp b/src/gui/components/filebrowser/juce_FileTreeComponent.cpp index 16dc423f31..bd835c36cf 100644 --- a/src/gui/components/filebrowser/juce_FileTreeComponent.cpp +++ b/src/gui/components/filebrowser/juce_FileTreeComponent.cpp @@ -141,7 +141,7 @@ public: void paintItem (Graphics& g, int width, int height) { - if (file != File::nonexistent && ! isDirectory) + if (file != File::nonexistent) { updateIcon (true); diff --git a/src/io/network/juce_Socket.cpp b/src/io/network/juce_Socket.cpp index d5e11ab9b5..cfc0401dd1 100644 --- a/src/io/network/juce_Socket.cpp +++ b/src/io/network/juce_Socket.cpp @@ -38,7 +38,7 @@ #include #include #else - #if MACOSX_DEPLOYMENT_TARGET <= MAC_OS_X_VERSION_10_4 + #if (MACOSX_DEPLOYMENT_TARGET <= MAC_OS_X_VERSION_10_4) && ! (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR) #include #endif #endif @@ -57,6 +57,12 @@ BEGIN_JUCE_NAMESPACE #include "../../threads/juce_ScopedLock.h" #include "../../threads/juce_Thread.h" +#if defined (JUCE_LINUX) || defined (JUCE_MAC) || defined (JUCE_IPHONE) + typedef socklen_t juce_socklen_t; +#else + typedef int juce_socklen_t; +#endif + //============================================================================== #if JUCE_WIN32 @@ -191,12 +197,7 @@ static int waitForReadiness (const int handle, const bool forReading, { int opt; - -#if defined (JUCE_LINUX) || defined (JUCE_MAC) - socklen_t len = sizeof (opt); -#else - int len = sizeof (opt); -#endif + juce_socklen_t len = sizeof (opt); if (getsockopt (handle, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0 || opt != 0) @@ -470,12 +471,7 @@ StreamingSocket* StreamingSocket::waitForNextConnection() const if (connected && isListener) { struct sockaddr address; - -#if defined (JUCE_LINUX) || defined (JUCE_MAC) - socklen_t len = sizeof (sockaddr); -#else - int len = sizeof (sockaddr); -#endif + juce_socklen_t len = sizeof (sockaddr); const int newSocket = (int) accept (handle, &address, &len); if (newSocket >= 0 && connected) @@ -580,12 +576,7 @@ bool DatagramSocket::connect (const String& remoteHostName, DatagramSocket* DatagramSocket::waitForNextConnection() const { struct sockaddr address; - -#if defined (JUCE_LINUX) || defined (JUCE_MAC) - socklen_t len = sizeof (sockaddr); -#else - int len = sizeof (sockaddr); -#endif + juce_socklen_t len = sizeof (sockaddr); while (waitUntilReady (true, -1) == 1) { diff --git a/src/juce_amalgamated_template.cpp b/src/juce_amalgamated_template.cpp index e296826927..3fcfb22f22 100644 --- a/src/juce_amalgamated_template.cpp +++ b/src/juce_amalgamated_template.cpp @@ -45,6 +45,8 @@ #include "native/windows/juce_win32_NativeIncludes.h" #elif defined (LINUX) #include "native/linux/juce_linux_NativeIncludes.h" +#elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + #include "native/mac/juce_iphone_NativeIncludes.h" #else #include "native/mac/juce_mac_NativeIncludes.h" #endif @@ -347,3 +349,7 @@ #if JUCE_MAC #include "native/juce_mac_NativeCode.mm" #endif + +#if JUCE_IPHONE + #include "native/juce_iphone_NativeCode.mm" +#endif diff --git a/src/native/juce_iphone_NativeCode.mm b/src/native/juce_iphone_NativeCode.mm new file mode 100644 index 0000000000..79b1c8ea87 --- /dev/null +++ b/src/native/juce_iphone_NativeCode.mm @@ -0,0 +1,111 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-9 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +/* + This file wraps together all the mac-specific code, so that + we can include all the native headers just once, and compile all our + platform-specific stuff in one big lump, keeping it out of the way of + the rest of the codebase. +*/ + +#include "mac/juce_iphone_NativeIncludes.h" + +BEGIN_JUCE_NAMESPACE + +//============================================================================== +#include "../core/juce_Singleton.h" +#include "../core/juce_Random.h" +#include "../core/juce_SystemStats.h" +#include "../threads/juce_Process.h" +#include "../threads/juce_Thread.h" +#include "../threads/juce_InterProcessLock.h" +#include "../io/files/juce_FileInputStream.h" +#include "../io/files/juce_NamedPipe.h" +#include "../io/network/juce_URL.h" +#include "../core/juce_PlatformUtilities.h" +#include "../text/juce_LocalisedStrings.h" +#include "../utilities/juce_DeletedAtShutdown.h" +#include "../application/juce_Application.h" +#include "../utilities/juce_SystemClipboard.h" +#include "../events/juce_MessageManager.h" +#include "../gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h" +#include "../gui/graphics/imaging/juce_ImageFileFormat.h" +#include "../gui/graphics/imaging/juce_CameraDevice.h" +#include "../gui/components/windows/juce_AlertWindow.h" +#include "../gui/components/juce_Desktop.h" +#include "../gui/components/menus/juce_MenuBarModel.h" +#include "../gui/components/special/juce_OpenGLComponent.h" +#include "../gui/components/special/juce_QuickTimeMovieComponent.h" +#include "../gui/components/mouse/juce_DragAndDropContainer.h" +#include "../gui/components/keyboard/juce_KeyPressMappingSet.h" +#include "../gui/components/special/juce_NSViewComponent.h" +#include "../gui/components/layout/juce_ComponentMovementWatcher.h" +#include "../gui/components/special/juce_WebBrowserComponent.h" +#include "../gui/components/filebrowser/juce_FileChooser.h" +#include "../audio/audio_file_formats/juce_AudioCDBurner.h" +#include "../audio/audio_file_formats/juce_AudioCDReader.h" +#include "../audio/audio_sources/juce_AudioSource.h" +#include "../audio/dsp/juce_AudioDataConverters.h" +#include "../audio/devices/juce_AudioIODeviceType.h" +#include "../audio/devices/juce_MidiOutput.h" +#include "../audio/devices/juce_MidiInput.h" +#undef Point + + +//============================================================================== +#define JUCE_INCLUDED_FILE 1 + +// Now include the actual code files.. + +#include "mac/juce_mac_ObjCSuffix.h" +#include "mac/juce_mac_Strings.mm" +#include "mac/juce_mac_SystemStats.mm" +#include "mac/juce_mac_Network.mm" +#include "common/juce_posix_NamedPipe.cpp" +#include "mac/juce_mac_Threads.mm" +#include "common/juce_posix_SharedCode.h" +#include "mac/juce_mac_Files.mm" +#include "mac/juce_iphone_MiscUtilities.mm" +#include "mac/juce_mac_Debugging.mm" + +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + /*#include "mac/juce_mac_NSViewComponentPeer.mm" + #include "mac/juce_mac_MouseCursor.mm" + #include "mac/juce_mac_NSViewComponent.mm" + #include "mac/juce_mac_AppleRemote.mm" + #include "mac/juce_mac_OpenGLComponent.mm" + #include "mac/juce_mac_MainMenu.mm" + #include "mac/juce_mac_FileChooser.mm" + #include "mac/juce_mac_QuickTimeMovieComponent.mm" + #include "mac/juce_mac_AudioCDBurner.mm" + #include "mac/juce_mac_Fonts.mm" + #include "mac/juce_mac_MessageManager.mm" + #include "mac/juce_mac_WebBrowserComponent.mm" + #include "mac/juce_mac_CoreAudio.cpp" + #include "mac/juce_mac_CoreMidi.cpp" + #include "mac/juce_mac_CameraDevice.mm"*/ +#endif + +END_JUCE_NAMESPACE diff --git a/src/native/juce_mac_NativeCode.mm b/src/native/juce_mac_NativeCode.mm index 12e593cd96..7c3e7a68c7 100644 --- a/src/native/juce_mac_NativeCode.mm +++ b/src/native/juce_mac_NativeCode.mm @@ -73,33 +73,12 @@ BEGIN_JUCE_NAMESPACE #include "../audio/devices/juce_MidiInput.h" #undef Point -//============================================================================== -/** This suffix is used for naming all Obj-C classes that are used inside juce. - - Because of the flat naming structure used by Obj-C, you can get horrible situations where - two DLLs are loaded into a host, each of which uses classes with the same names, and these get - cross-linked so that when you make a call to a class that you thought was private, it ends up - actually calling into a similarly named class in the other module's address space. - - By changing this macro to a unique value, you ensure that all the obj-C classes in your app - have unique names, and should avoid this problem. - - If you're using the amalgamated version, you can just set this macro to something unique before - you include juce_amalgamated.cpp. -*/ -#ifndef JUCE_ObjCExtraSuffix - #define JUCE_ObjCExtraSuffix 3 -#endif - -#define appendMacro1(a, b, c, d) a ## _ ## b ## _ ## c ## _ ## d -#define appendMacro2(a, b, c, d) appendMacro1(a, b, c, d) -#define MakeObjCClassName(rootName) appendMacro2 (rootName, JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_ObjCExtraSuffix) - //============================================================================== #define JUCE_INCLUDED_FILE 1 // Now include the actual code files.. +#include "mac/juce_mac_ObjCSuffix.h" #include "mac/juce_mac_Strings.mm" #include "mac/juce_mac_SystemStats.mm" #include "mac/juce_mac_Network.mm" diff --git a/src/native/linux/juce_linux_Network.cpp b/src/native/linux/juce_linux_Network.cpp index 5e03523f5e..d3dba90d16 100644 --- a/src/native/linux/juce_linux_Network.cpp +++ b/src/native/linux/juce_linux_Network.cpp @@ -54,7 +54,7 @@ int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littl { int64 a = 0; for (int j = 6; --j >= 0;) - a = (a << 8) | (uint8) ifr.ifr_hwaddr.sa_data[j]; + a = (a << 8) | (uint8) ifr.ifr_hwaddr.sa_data [littleEndian ? j : (5 - j)]; *addresses++ = a; ++numResults; diff --git a/src/native/mac/juce_iphone_MiscUtilities.mm b/src/native/mac/juce_iphone_MiscUtilities.mm new file mode 100644 index 0000000000..0da5e8b18c --- /dev/null +++ b/src/native/mac/juce_iphone_MiscUtilities.mm @@ -0,0 +1,135 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-9 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + + +//============================================================================== +ScopedAutoReleasePool::ScopedAutoReleasePool() +{ + pool = [[NSAutoreleasePool alloc] init]; +} + +ScopedAutoReleasePool::~ScopedAutoReleasePool() +{ + [((NSAutoreleasePool*) pool) release]; +} + +//============================================================================== +void PlatformUtilities::beep() +{ + //xxx + //AudioServicesPlaySystemSound (); +} + +//============================================================================== +void PlatformUtilities::addItemToDock (const File& file) +{ +} + + +//============================================================================== +#if ! JUCE_ONLY_BUILD_CORE_LIBRARY + +bool AlertWindow::showNativeDialogBox (const String& title, + const String& bodyText, + bool isOkCancel) +{ + const ScopedAutoReleasePool pool; + + UIAlertView *alert = [[[UIAlertView alloc] initWithTitle: juceStringToNS (title) + message: juceStringToNS (title) + delegate: nil + cancelButtonTitle: @"OK" + otherButtonTitles: (isOkCancel ? @"Cancel" : nil), nil] autorelease]; + alert.cancelButtonIndex = alert.firstOtherButtonIndex; + [alert show]; + + // xxx need to use a delegate to find which button was clicked + return false; +} + +//============================================================================== +bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, const bool canMoveFiles) +{ + jassertfalse // not implemented! + return false; +} + +bool DragAndDropContainer::performExternalDragDropOfText (const String& text) +{ + jassertfalse // not implemented! + return false; +} + +//============================================================================== +bool Desktop::canUseSemiTransparentWindows() throw() +{ + return true; +} + +void Desktop::getMousePosition (int& x, int& y) throw() +{ + x = 0; + y = 0; +} + +void Desktop::setMousePosition (int x, int y) throw() +{ +} + +//============================================================================== +void Desktop::setScreenSaverEnabled (const bool isEnabled) throw() +{ + [[UIApplication sharedApplication] setIdleTimerDisabled: ! isEnabled]; +} + +bool Desktop::isScreenSaverEnabled() throw() +{ + return ! [[UIApplication sharedApplication] isIdleTimerDisabled]; +} + + +//============================================================================== +void juce_updateMultiMonitorInfo (Array & monitorCoords, const bool clipToWorkArea) throw() +{ + const ScopedAutoReleasePool pool; + monitorCoords.clear(); + + CGRect r = clipToWorkArea ? [[UIScreen mainScreen] applicationFrame] + : [[UIScreen mainScreen] bounds]; + + monitorCoords.add (Rectangle ((int) r.origin.x, + (int) r.origin.y, + (int) r.size.width, + (int) r.size.height)); +} + + +#endif + +#endif diff --git a/src/native/mac/juce_iphone_NativeIncludes.h b/src/native/mac/juce_iphone_NativeIncludes.h new file mode 100644 index 0000000000..481125738f --- /dev/null +++ b/src/native/mac/juce_iphone_NativeIncludes.h @@ -0,0 +1,58 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-9 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifndef __JUCE_MAC_NATIVEINCLUDES_JUCEHEADER__ +#define __JUCE_MAC_NATIVEINCLUDES_JUCEHEADER__ + +/* + This file wraps together all the mac-specific code, so that + we can include all the native headers just once, and compile all our + platform-specific stuff in one big lump, keeping it out of the way of + the rest of the codebase. +*/ + +#include "../../core/juce_StandardHeader.h" + +#import +#import +#import +#import +#import +#import +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#endif // __JUCE_MAC_NATIVEINCLUDES_JUCEHEADER__ diff --git a/src/native/mac/juce_mac_Files.mm b/src/native/mac/juce_mac_Files.mm index bb9180d32b..c60aed8bdc 100644 --- a/src/native/mac/juce_mac_Files.mm +++ b/src/native/mac/juce_mac_Files.mm @@ -33,28 +33,6 @@ */ //============================================================================== -const unsigned int macTimeToUnixTimeDiff = 0x7c25be90; - -static uint64 utcDateTimeToUnixTime (const UTCDateTime& d) throw() -{ - if (d.highSeconds == 0 && d.lowSeconds == 0 && d.fraction == 0) - return 0; - - return (((((uint64) d.highSeconds) << 32) | (uint64) d.lowSeconds) * 1000) - + ((d.fraction * 1000) >> 16) - - 2082844800000ll; -} - -static void unixTimeToUtcDateTime (uint64 t, UTCDateTime& d) throw() -{ - if (t != 0) - t += 2082844800000ll; - - d.highSeconds = (t / 1000) >> 32; - d.lowSeconds = (t / 1000) & (uint64) 0xffffffff; - d.fraction = ((t % 1000) << 16) / 1000; -} - void juce_getFileTimes (const String& fileName, int64& modificationTime, int64& accessTime, @@ -64,24 +42,13 @@ void juce_getFileTimes (const String& fileName, accessTime = 0; creationTime = 0; - FSRef fileRef; - if (PlatformUtilities::makeFSRefFromPath (&fileRef, fileName)) + struct stat info; + const int res = stat (fileName.toUTF8(), &info); + if (res == 0) { - FSRefParam info; - zerostruct (info); - - info.ref = &fileRef; - info.whichInfo = kFSCatInfoAllDates; - - FSCatalogInfo catInfo; - info.catInfo = &catInfo; - - if (PBGetCatalogInfoSync (&info) == noErr) - { - creationTime = utcDateTimeToUnixTime (catInfo.createDate); - accessTime = utcDateTimeToUnixTime (catInfo.accessDate); - modificationTime = utcDateTimeToUnixTime (catInfo.contentModDate); - } + modificationTime = (int64) info.st_mtime * 1000; + accessTime = (int64) info.st_atime * 1000; + creationTime = (int64) info.st_ctime * 1000; } } @@ -90,59 +57,29 @@ bool juce_setFileTimes (const String& fileName, int64 accessTime, int64 creationTime) throw() { - FSRef fileRef; - if (PlatformUtilities::makeFSRefFromPath (&fileRef, fileName)) - { - FSRefParam info; - zerostruct (info); + struct utimbuf times; + times.actime = (time_t) (accessTime / 1000); + times.modtime = (time_t) (modificationTime / 1000); - info.ref = &fileRef; - info.whichInfo = kFSCatInfoAllDates; - - FSCatalogInfo catInfo; - info.catInfo = &catInfo; - - if (PBGetCatalogInfoSync (&info) == noErr) - { - if (creationTime != 0) - unixTimeToUtcDateTime (creationTime, catInfo.createDate); - - if (modificationTime != 0) - unixTimeToUtcDateTime (modificationTime, catInfo.contentModDate); - - if (accessTime != 0) - unixTimeToUtcDateTime (accessTime, catInfo.accessDate); - - return PBSetCatalogInfoSync (&info) == noErr; - } - } - - return false; + return utime (fileName.toUTF8(), ×) == 0; } bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) throw() { - const char* const fileNameUTF8 = fileName.toUTF8(); - struct stat info; - const int res = stat (fileNameUTF8, &info); + const int res = stat (fileName.toUTF8(), &info); + if (res != 0) + return false; - bool ok = false; + info.st_mode &= 0777; // Just permissions - if (res == 0) - { - info.st_mode &= 0777; // Just permissions + if (isReadOnly) + info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + else + // Give everybody write permission? + info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; - if (isReadOnly) - info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); - else - // Give everybody write permission? - info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH; - - ok = chmod (fileNameUTF8, info.st_mode) == 0; - } - - return ok; + return chmod (fileName.toUTF8(), info.st_mode) == 0; } bool juce_copyFile (const String& src, const String& dst) throw() @@ -202,22 +139,29 @@ bool File::isOnHardDisk() const throw() bool File::isOnRemovableDrive() const throw() { - const ScopedAutoReleasePool pool; - BOOL removable = false; +#if JUCE_IPHONE + return false; // xxx is this possible? +#else + const ScopedAutoReleasePool pool; + BOOL removable = false; - [[NSWorkspace sharedWorkspace] - getFileSystemInfoForPath: juceStringToNS (getFullPathName()) - isRemovable: &removable - isWritable: nil - isUnmountable: nil - description: nil - type: nil]; + [[NSWorkspace sharedWorkspace] + getFileSystemInfoForPath: juceStringToNS (getFullPathName()) + isRemovable: &removable + isWritable: nil + isUnmountable: nil + description: nil + type: nil]; return removable; +#endif } static bool juce_isHiddenFile (const String& path) throw() { +#if JUCE_IPHONE + return File (path).getFileName().startsWithChar (T('.')); +#else FSRef ref; if (! PlatformUtilities::makeFSRefFromPath (&ref, path)) return false; @@ -229,6 +173,7 @@ static bool juce_isHiddenFile (const String& path) throw() return (((FolderInfo*) &info.finderInfo)->finderFlags & kIsInvisible) != 0; return (((FileInfo*) &info.finderInfo)->finderFlags & kIsInvisible) != 0; +#endif } bool File::isHidden() const throw() @@ -350,15 +295,10 @@ const String File::getVersion() const throw() //============================================================================== const File File::getLinkedTarget() const throw() { - FSRef ref; - Boolean targetIsAFolder, wasAliased; + NSString* dest = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath: juceStringToNS (getFullPathName()) error: nil]; - if (PlatformUtilities::makeFSRefFromPath (&ref, getFullPathName()) - && (FSResolveAliasFileWithMountFlags (&ref, true, &targetIsAFolder, &wasAliased, 0) == noErr) - && wasAliased) - { - return File (PlatformUtilities::makePathFromFSRef (&ref)); - } + if (dest != nil) + return File (nsStringToJuce (dest)); return *this; } @@ -369,6 +309,9 @@ bool File::moveToTrash() const throw() if (! exists()) return true; +#if JUCE_IPHONE + return deleteFile(); //xxx is there a trashcan on the iPhone? +#else const ScopedAutoReleasePool pool; NSString* p = juceStringToNS (getFullPathName()); @@ -379,100 +322,36 @@ bool File::moveToTrash() const throw() destination: @"" files: [NSArray arrayWithObject: [p lastPathComponent]] tag: nil ]; +#endif } //============================================================================== struct FindFileStruct { - String parentDir, wildCard; - DIR* dir; - - bool getNextMatch (String& result, bool* const isDir, bool* const isHidden, int64* const fileSize, - Time* const modTime, Time* const creationTime, bool* const isReadOnly) throw() - { - const char* const wildCardUTF8 = wildCard.toUTF8(); - - for (;;) - { - struct dirent* const de = readdir (dir); - - if (de == 0) - break; - - if (fnmatch (wildCardUTF8, de->d_name, 0) == 0) - { - result = String::fromUTF8 ((const uint8*) de->d_name); - - const String path (parentDir + result); - - if (isDir != 0 || fileSize != 0) - { - struct stat info; - const bool statOk = juce_stat (path, info); - - if (isDir != 0) - *isDir = path.isEmpty() || (statOk && ((info.st_mode & S_IFDIR) != 0)); - - if (isHidden != 0) - *isHidden = (de->d_name[0] == '.') - || juce_isHiddenFile (path); - - if (fileSize != 0) - *fileSize = statOk ? info.st_size : 0; - } - - if (modTime != 0 || creationTime != 0) - { - int64 m, a, c; - juce_getFileTimes (path, m, a, c); - - if (modTime != 0) - *modTime = m; - - if (creationTime != 0) - *creationTime = c; - } - - if (isReadOnly != 0) - *isReadOnly = ! juce_canWriteToFile (path); - - return true; - } - } - - return false; - } + NSDirectoryEnumerator* enumerator; + String parentDir; }; -// returns 0 on failure void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) throw() { - DIR* const d = opendir (directory.toUTF8()); + NSDirectoryEnumerator* e = [[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory)]; - if (d != 0) + if (e != 0) { - FindFileStruct* const ff = new FindFileStruct(); + FindFileStruct* ff = new FindFileStruct(); + ff->enumerator = [e retain]; ff->parentDir = directory; - if (!ff->parentDir.endsWithChar (File::separator)) + if (! ff->parentDir.endsWithChar (File::separator)) ff->parentDir += File::separator; - ff->wildCard = wildCard; - ff->dir = d; - - if (ff->getNextMatch (firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly)) - { + if (juce_findFileNext (ff, firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly)) return ff; - } - else - { - firstResultFile = String::empty; - isDir = false; - closedir (d); - delete ff; - } + + [e release]; + delete ff; } return 0; @@ -481,23 +360,55 @@ void* juce_findFileStart (const String& directory, const String& wildCard, Strin bool juce_findFileNext (void* handle, String& resultFile, bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) throw() { - FindFileStruct* const ff = (FindFileStruct*) handle; + FindFileStruct* ff = (FindFileStruct*) handle; + NSString* file; - if (ff != 0) - return ff->getNextMatch (resultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); + if (ff == 0 || (file = [ff->enumerator nextObject]) == 0) + return false; - return false; + [ff->enumerator skipDescendents]; + resultFile = nsStringToJuce (file); + + const String path (ff->parentDir + resultFile); + + if (isDir != 0 || fileSize != 0) + { + struct stat info; + const bool statOk = juce_stat (path, info); + + if (isDir != 0) + *isDir = statOk && ((info.st_mode & S_IFDIR) != 0); + + if (isHidden != 0) + *isHidden = juce_isHiddenFile (path); + + if (fileSize != 0) + *fileSize = statOk ? info.st_size : 0; + } + + if (modTime != 0 || creationTime != 0) + { + int64 m, a, c; + juce_getFileTimes (path, m, a, c); + + if (modTime != 0) + *modTime = m; + + if (creationTime != 0) + *creationTime = c; + } + + if (isReadOnly != 0) + *isReadOnly = ! juce_canWriteToFile (path); + + return true; } void juce_findFileClose (void* handle) throw() { - FindFileStruct* const ff = (FindFileStruct*)handle; - - if (ff != 0) - { - closedir (ff->dir); - delete ff; - } + FindFileStruct* ff = (FindFileStruct*) handle; + [ff->enumerator release]; + delete ff; } //============================================================================== @@ -525,6 +436,9 @@ bool juce_launchExecutable (const String& pathAndArguments) throw() bool juce_launchFile (const String& fileName, const String& parameters) throw() { +#if JUCE_IPHONE + return false; // is this possible? +#else const ScopedAutoReleasePool pool; if (parameters.isEmpty()) @@ -560,9 +474,11 @@ bool juce_launchFile (const String& fileName, } return ok; +#endif } //============================================================================== +#if ! JUCE_IPHONE bool PlatformUtilities::makeFSRefFromPath (FSRef* destFSRef, const String& path) { return FSPathMakeRef ((const UInt8*) path.toUTF8(), destFSRef, 0) == noErr; @@ -580,18 +496,24 @@ const String PlatformUtilities::makePathFromFSRef (FSRef* file) return PlatformUtilities::convertToPrecomposedUnicode (result); } +#endif //============================================================================== OSType PlatformUtilities::getTypeOfFile (const String& filename) { const ScopedAutoReleasePool pool; - return NSHFSTypeCodeFromFileType (NSHFSTypeOfFile (juceStringToNS (filename))); + NSDictionary* fileDict = [[NSFileManager defaultManager] fileAttributesAtPath: juceStringToNS (filename) traverseLink: NO]; + return (OSType) [fileDict objectForKey: NSFileHFSTypeCode]; } bool PlatformUtilities::isBundle (const String& filename) { +#if JUCE_IPHONE + return false; // xxx can't find a sensible way to do this without trying to open the bundle.. +#else const ScopedAutoReleasePool pool; return [[NSWorkspace sharedWorkspace] isFilePackageAtPath: juceStringToNS (filename)]; +#endif } #endif diff --git a/src/native/mac/juce_mac_NativeIncludes.h b/src/native/mac/juce_mac_NativeIncludes.h index 18dc326f08..381f4a6982 100644 --- a/src/native/mac/juce_mac_NativeIncludes.h +++ b/src/native/mac/juce_mac_NativeIncludes.h @@ -46,9 +46,6 @@ #import #import #import -#import -#import -#import #import #include @@ -59,6 +56,9 @@ #include #include #include +#include +#include +#include #if MACOS_10_4_OR_EARLIER #include diff --git a/src/native/mac/juce_mac_Network.mm b/src/native/mac/juce_mac_Network.mm index 388265a7f9..36786f9e4b 100644 --- a/src/native/mac/juce_mac_Network.mm +++ b/src/native/mac/juce_mac_Network.mm @@ -28,84 +28,42 @@ #if JUCE_INCLUDED_FILE //============================================================================== -static bool getEthernetIterator (io_iterator_t* matchingServices) throw() -{ - mach_port_t masterPort; - - if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS) - { - CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass); - - if (dict != 0) - { - CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault, - 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - - if (propDict != 0) - { - CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue); - - CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict); - CFRelease (propDict); - } - } - - return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS; - } - - return false; -} - int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() { + #ifndef IFT_ETHER + #define IFT_ETHER 6 + #endif + + ifaddrs* addrs = 0; int numResults = 0; - io_iterator_t it; - if (getEthernetIterator (&it)) + if (getifaddrs (&addrs) == 0) { - io_object_t i; + const ifaddrs* cursor = addrs; - while ((i = IOIteratorNext (it)) != 0) + while (cursor != 0 && numResults < maxNum) { - io_object_t controller; - - if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS) + if (cursor->ifa_addr->sa_family == AF_LINK) { - CFTypeRef data = IORegistryEntryCreateCFProperty (controller, - CFSTR (kIOMACAddress), - kCFAllocatorDefault, - 0); - if (data != 0) + const sockaddr_dl* const sadd = (const sockaddr_dl*) cursor->ifa_addr; + + if (sadd->sdl_type == IFT_ETHER) { - UInt8 addr [kIOEthernetAddressSize]; - zeromem (addr, sizeof (addr)); + const uint8* const addr = ((const uint8*) sadd->sdl_data) + sadd->sdl_nlen; - CFDataGetBytes ((CFDataRef) data, CFRangeMake (0, sizeof (addr)), addr); - CFRelease (data); - - int64 a = 0; + uint64 a = 0; for (int i = 6; --i >= 0;) - a = (a << 8) | addr[i]; + a = (a << 8) | addr [littleEndian ? i : (5 - i)]; - if (! littleEndian) - a = (int64) swapByteOrder ((uint64) a); - - if (numResults < maxNum) - { - *addresses++ = a; - ++numResults; - } + *addresses++ = (int64) a; + ++numResults; } - - IOObjectRelease (controller); } - IOObjectRelease (i); + cursor = cursor->ifa_next; } - IOObjectRelease (it); + freeifaddrs (addrs); } return numResults; @@ -117,6 +75,11 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd const String& bodyText, const StringArray& filesToAttach) { +#if JUCE_IPHONE + //xxx probably need to use MFMailComposeViewController + jassertfalse + return false; +#else const ScopedAutoReleasePool pool; String script; @@ -152,6 +115,7 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd [s release]; return ok; +#endif } //============================================================================== diff --git a/src/native/mac/juce_mac_ObjCSuffix.h b/src/native/mac/juce_mac_ObjCSuffix.h new file mode 100644 index 0000000000..64e8b3a55f --- /dev/null +++ b/src/native/mac/juce_mac_ObjCSuffix.h @@ -0,0 +1,46 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-9 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + + +/** This suffix is used for naming all Obj-C classes that are used inside juce. + + Because of the flat naming structure used by Obj-C, you can get horrible situations where + two DLLs are loaded into a host, each of which uses classes with the same names, and these get + cross-linked so that when you make a call to a class that you thought was private, it ends up + actually calling into a similarly named class in the other module's address space. + + By changing this macro to a unique value, you ensure that all the obj-C classes in your app + have unique names, and should avoid this problem. + + If you're using the amalgamated version, you can just set this macro to something unique before + you include juce_amalgamated.cpp. +*/ +#ifndef JUCE_ObjCExtraSuffix + #define JUCE_ObjCExtraSuffix 3 +#endif + +#define appendMacro1(a, b, c, d) a ## _ ## b ## _ ## c ## _ ## d +#define appendMacro2(a, b, c, d) appendMacro1(a, b, c, d) +#define MakeObjCClassName(rootName) appendMacro2 (rootName, JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_ObjCExtraSuffix) diff --git a/src/native/mac/juce_mac_Strings.mm b/src/native/mac/juce_mac_Strings.mm index 153ab017af..cd11998e44 100644 --- a/src/native/mac/juce_mac_Strings.mm +++ b/src/native/mac/juce_mac_Strings.mm @@ -103,6 +103,10 @@ CFStringRef PlatformUtilities::juceStringToCFString (const String& s) const String PlatformUtilities::convertToPrecomposedUnicode (const String& s) { +#if JUCE_IPHONE + const ScopedAutoReleasePool pool; + return nsStringToJuce ([juceStringToNS (s) precomposedStringWithCanonicalMapping]); +#else UnicodeMapping map; map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault, @@ -156,6 +160,7 @@ const String PlatformUtilities::convertToPrecomposedUnicode (const String& s) } return result; +#endif } //============================================================================== @@ -163,16 +168,25 @@ const String PlatformUtilities::convertToPrecomposedUnicode (const String& s) void SystemClipboard::copyTextToClipboard (const String& text) throw() { +#if JUCE_IPHONE + [[UIPasteboard generalPasteboard] setValue: juceStringToNS (text) + forPasteboardType: (NSString*) kUTTypePlainText]; +#else [[NSPasteboard generalPasteboard] declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: nil]; [[NSPasteboard generalPasteboard] setString: juceStringToNS (text) forType: NSStringPboardType]; +#endif } const String SystemClipboard::getTextFromClipboard() throw() { +#if JUCE_IPHONE + NSString* text = [[UIPasteboard generalPasteboard] valueForPasteboardType: (NSString*) kUTTypePlainText]; +#else NSString* text = [[NSPasteboard generalPasteboard] stringForType: NSStringPboardType]; +#endif return text == 0 ? String::empty : nsStringToJuce (text); diff --git a/src/native/mac/juce_mac_SystemStats.mm b/src/native/mac/juce_mac_SystemStats.mm index 8dd1eff6ab..32e32e080d 100644 --- a/src/native/mac/juce_mac_SystemStats.mm +++ b/src/native/mac/juce_mac_SystemStats.mm @@ -27,7 +27,8 @@ // compiled on its own). #ifdef JUCE_INCLUDED_FILE -static int64 highResTimerFrequency; +static int64 highResTimerFrequency = 0; +static double highResTimerToMillisecRatio = 0; #if JUCE_INTEL @@ -83,11 +84,13 @@ void SystemStats::initialiseStats() throw() { initialised = true; +#if JUCE_MAC // extremely annoying: adding this line stops the apple menu items from working. Of // course, not adding it means that carbon windows (e.g. in plugins) won't get // any events. //NSApplicationLoad(); [NSApplication sharedApplication]; +#endif #if JUCE_INTEL { @@ -101,7 +104,10 @@ void SystemStats::initialiseStats() throw() } #endif - highResTimerFrequency = (int64) AudioGetHostClockFrequency(); + mach_timebase_info_data_t timebase; + (void) mach_timebase_info (&timebase); + highResTimerFrequency = (int64) (1.0e9 * timebase.denom / timebase.numer); + highResTimerToMillisecRatio = timebase.numer / (1.0e6 * timebase.denom); String s (SystemStats::getJUCEVersion()); @@ -213,27 +219,19 @@ int SystemStats::getNumCpus() throw() } //============================================================================== -static int64 juce_getMicroseconds() throw() -{ - UnsignedWide t; - Microseconds (&t); - return (((int64) t.hi) << 32) | t.lo; -} - uint32 juce_millisecondsSinceStartup() throw() { - return (uint32) (juce_getMicroseconds() / 1000); + return (uint32) (mach_absolute_time() * highResTimerToMillisecRatio); } double Time::getMillisecondCounterHiRes() throw() { - // xxx might be more accurate to use a scaled AudioGetCurrentHostTime? - return juce_getMicroseconds() * 0.001; + return mach_absolute_time() * highResTimerToMillisecRatio; } int64 Time::getHighResolutionTicks() throw() { - return (int64) AudioGetCurrentHostTime(); + return (int64) mach_absolute_time(); } int64 Time::getHighResolutionTicksPerSecond() throw() @@ -243,7 +241,7 @@ int64 Time::getHighResolutionTicksPerSecond() throw() int64 SystemStats::getClockCycleCounter() throw() { - return (int64) AudioGetCurrentHostTime(); + return (int64) mach_absolute_time(); } bool Time::setSystemTimeToThisTime() const throw() diff --git a/src/native/mac/juce_mac_Threads.mm b/src/native/mac/juce_mac_Threads.mm index 8ca5ba616a..15529adf7a 100644 --- a/src/native/mac/juce_mac_Threads.mm +++ b/src/native/mac/juce_mac_Threads.mm @@ -97,7 +97,11 @@ void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) throw() //============================================================================== bool Process::isForegroundProcess() throw() { +#if JUCE_MAC return [NSApp isActive]; +#else + return true; // xxx change this if more than one app is ever possible on the iPhone! +#endif } void Process::raisePrivilege() diff --git a/src/native/windows/juce_win32_Network.cpp b/src/native/windows/juce_win32_Network.cpp index f49bdf21fd..72ecbf1bdd 100644 --- a/src/native/windows/juce_win32_Network.cpp +++ b/src/native/windows/juce_win32_Network.cpp @@ -338,12 +338,9 @@ static int getMACAddressesViaNetBios (int64* addresses, int maxNum, const bool l { if (astat.adapt.adapter_type == 0xfe) { - int64 mac = 0; - for (unsigned int i = 0; i < 6; ++i) - mac = (mac << 8) | astat.adapt.adapter_address[i]; - - if (littleEndian) - mac = (int64) swapByteOrder ((uint64) mac); + uint64 mac = 0; + for (int i = 6; --i >= 0;) + mac = (mac << 8) | astat.adapt.adapter_address [littleEndian ? i : (5 - i)]; if (numFound < maxNum && mac != 0) addresses [numFound++] = mac; diff --git a/src/utilities/juce_PropertiesFile.cpp b/src/utilities/juce_PropertiesFile.cpp index 70ff717a58..6f7b42bd74 100644 --- a/src/utilities/juce_PropertiesFile.cpp +++ b/src/utilities/juce_PropertiesFile.cpp @@ -268,7 +268,7 @@ const File PropertiesFile::getDefaultAppSettingsFile (const String& applicationN // mustn't have illegal characters in this name.. jassert (applicationName == File::createLegalFileName (applicationName)); -#if JUCE_MAC +#if JUCE_MAC || JUCE_IPHONE File dir (commonToAllUsers ? "/Library/Preferences" : "~/Library/Preferences");