2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
20 ==============================================================================
23 #include "juce_mac_CFHelpers.h"
25 /* This file contains a few helper functions that are used internally but which
26 need to be kept away from the public headers because they use obj-C symbols.
31 //==============================================================================
32 inline Range
<int> nsRangeToJuce (NSRange range
)
34 return { (int) range
.location
, (int) (range
.location
+ range
.length
) };
37 inline NSRange
juceRangeToNS (Range
<int> range
)
39 return NSMakeRange ((NSUInteger
) range
.getStart(), (NSUInteger
) range
.getLength());
42 inline String
nsStringToJuce (NSString
* s
)
44 return CharPointer_UTF8 ([s UTF8String
]);
47 inline NSString
* juceStringToNS (const String
& s
)
49 return [NSString stringWithUTF8String
: s
.toUTF8()];
52 inline NSString
* nsStringLiteral (const char* const s
) noexcept
54 return [NSString stringWithUTF8String
: s
];
57 inline NSString
* nsEmptyString() noexcept
59 return [NSString string
];
62 inline NSURL
* createNSURLFromFile (const String
& f
)
64 return [NSURL fileURLWithPath
: juceStringToNS (f
)];
67 inline NSURL
* createNSURLFromFile (const File
& f
)
69 return createNSURLFromFile (f
.getFullPathName());
72 inline NSArray
* createNSArrayFromStringArray (const StringArray
& strings
)
74 auto array
= [[NSMutableArray alloc
] init
];
76 for (auto string
: strings
)
77 [array addObject
:juceStringToNS (string
)];
79 return [array autorelease
];
82 inline NSArray
* varArrayToNSArray (const var
& varToParse
);
84 inline NSDictionary
* varObjectToNSDictionary (const var
& varToParse
)
86 auto dictionary
= [NSMutableDictionary dictionary
];
88 if (varToParse
.isObject())
90 auto* dynamicObject
= varToParse
.getDynamicObject();
92 auto& properties
= dynamicObject
->getProperties();
94 for (int i
= 0; i
< properties
.size(); ++i
)
96 auto* keyString
= juceStringToNS (properties
.getName (i
).toString());
98 const var
& valueVar
= properties
.getValueAt (i
);
100 if (valueVar
.isObject())
102 auto* valueDictionary
= varObjectToNSDictionary (valueVar
);
104 [dictionary setObject
: valueDictionary forKey
: keyString
];
106 else if (valueVar
.isArray())
108 auto* valueArray
= varArrayToNSArray (valueVar
);
110 [dictionary setObject
: valueArray forKey
: keyString
];
114 auto* valueString
= juceStringToNS (valueVar
.toString());
116 [dictionary setObject
: valueString forKey
: keyString
];
124 inline NSArray
* varArrayToNSArray (const var
& varToParse
)
126 jassert (varToParse
.isArray());
128 if (! varToParse
.isArray())
131 const auto* varArray
= varToParse
.getArray();
133 auto array
= [NSMutableArray arrayWithCapacity
: (NSUInteger
) varArray
->size()];
135 for (const auto& aVar
: *varArray
)
139 auto* valueDictionary
= varObjectToNSDictionary (aVar
);
141 [array addObject
: valueDictionary
];
143 else if (aVar
.isArray())
145 auto* valueArray
= varArrayToNSArray (aVar
);
147 [array addObject
: valueArray
];
151 auto* valueString
= juceStringToNS (aVar
.toString());
153 [array addObject
: valueString
];
160 var
nsObjectToVar (NSObject
* array
);
162 inline var
nsDictionaryToVar (NSDictionary
* dictionary
)
164 DynamicObject::Ptr
dynamicObject (new DynamicObject());
166 for (NSString
* key in dictionary
)
167 dynamicObject
->setProperty (nsStringToJuce (key
), nsObjectToVar ([dictionary objectForKey
: key
]));
169 return var (dynamicObject
.get());
172 inline var
nsArrayToVar (NSArray
* array
)
174 Array
<var
> resultArray
;
176 for (id value in array
)
177 resultArray
.add (nsObjectToVar (value
));
179 return var (resultArray
);
182 inline var
nsObjectToVar (NSObject
* obj
)
184 if ([obj isKindOfClass
: [NSString
class]]) return nsStringToJuce ((NSString
*) obj
);
185 else if ([obj isKindOfClass
: [NSNumber
class]]) return nsStringToJuce ([(NSNumber
*) obj stringValue
]);
186 else if ([obj isKindOfClass
: [NSDictionary
class]]) return nsDictionaryToVar ((NSDictionary
*) obj
);
187 else if ([obj isKindOfClass
: [NSArray
class]]) return nsArrayToVar ((NSArray
*) obj
);
190 // Unsupported yet, add here!
198 template <typename RectangleType
>
199 NSRect
makeNSRect (const RectangleType
& r
) noexcept
201 return NSMakeRect (static_cast<CGFloat
> (r
.getX()),
202 static_cast<CGFloat
> (r
.getY()),
203 static_cast<CGFloat
> (r
.getWidth()),
204 static_cast<CGFloat
> (r
.getHeight()));
209 template <typename T
>
213 static constexpr auto value
= sizeof (T
) > 8;
215 static constexpr auto value
= sizeof (T
) > 16;
220 struct NeedsStret
<void> { static constexpr auto value
= false; };
222 template <typename T
, bool b
= NeedsStret
<T
>::value
>
223 struct MetaSuperFn
{ static constexpr auto value
= objc_msgSendSuper_stret
; };
225 template <typename T
>
226 struct MetaSuperFn
<T
, false> { static constexpr auto value
= objc_msgSendSuper
; };
229 struct MetaSuperFn
{ static constexpr auto value
= objc_msgSendSuper
; };
232 template <typename SuperType
, typename ReturnType
, typename
... Params
>
233 inline ReturnType
ObjCMsgSendSuper (id self
, SEL sel
, Params
... params
)
235 using SuperFn
= ReturnType (*) (struct objc_super
*, SEL
, Params
...);
236 const auto fn
= reinterpret_cast<SuperFn
> (MetaSuperFn
<ReturnType
>::value
);
238 objc_super s
= { self
, [SuperType
class] };
239 return fn (&s
, sel
, params
...);
242 //==============================================================================
243 struct NSObjectDeleter
245 void operator() (NSObject
* object
) const noexcept
247 if (object
!= nullptr)
252 template <typename NSType
>
253 using NSUniquePtr
= std::unique_ptr
<NSType
, NSObjectDeleter
>;
255 /* This has very similar semantics to NSUniquePtr, with the main difference that it doesn't
256 automatically add a pointer to the managed type. This makes it possible to declare
257 scoped handles to id or block types.
259 template <typename T
>
260 class ObjCObjectHandle
263 ObjCObjectHandle() = default;
265 // Note that this does *not* retain the argument.
266 explicit ObjCObjectHandle (T ptr
) : item (ptr
) {}
268 ~ObjCObjectHandle() noexcept
{ reset(); }
270 ObjCObjectHandle (const ObjCObjectHandle
& other
)
277 ObjCObjectHandle
& operator= (const ObjCObjectHandle
& other
)
284 ObjCObjectHandle (ObjCObjectHandle
&& other
) noexcept
{ swap (other
); }
286 ObjCObjectHandle
& operator= (ObjCObjectHandle
&& other
) noexcept
293 // Note that this does *not* retain the argument.
294 void reset (T ptr
) { *this = ObjCObjectHandle
{ ptr
}; }
296 T
get() const { return item
; }
306 bool operator== (const ObjCObjectHandle
& other
) const { return item
== other
.item
; }
307 bool operator!= (const ObjCObjectHandle
& other
) const { return ! (*this == other
); }
309 bool operator== (std::nullptr_t
) const { return item
== nullptr; }
310 bool operator!= (std::nullptr_t
) const { return ! (*this == nullptr); }
313 void swap (ObjCObjectHandle
& other
) noexcept
{ std::swap (other
.item
, item
); }
318 //==============================================================================
321 constexpr auto makeCompileTimeStr()
323 return std::array
<char, 1> { { '\0' } };
326 template <typename A
, size_t... As
, typename B
, size_t... Bs
>
327 constexpr auto joinCompileTimeStrImpl (A
&& a
, std::index_sequence
<As
...>,
328 B
&& b
, std::index_sequence
<Bs
...>)
330 return std::array
<char, sizeof... (As
) + sizeof... (Bs
) + 1> { { a
[As
]..., b
[Bs
]..., '\0' } };
333 template <size_t A
, size_t B
>
334 constexpr auto joinCompileTimeStr (const char (&a
)[A
], std::array
<char, B
> b
)
336 return joinCompileTimeStrImpl (a
, std::make_index_sequence
<A
- 1>(),
337 b
, std::make_index_sequence
<B
- 1>());
340 template <size_t A
, typename
... Others
>
341 constexpr auto makeCompileTimeStr (const char (&v
)[A
], Others
&&... others
)
343 return joinCompileTimeStr (v
, makeCompileTimeStr (others
...));
345 } // namespace detail
347 //==============================================================================
348 template <typename Type
>
349 inline Type
getIvar (id self
, const char* name
)
352 object_getInstanceVariable (self
, name
, &v
);
353 return static_cast<Type
> (v
);
356 template <typename SuperclassType
>
359 ObjCClass (const char* nameRoot
)
360 : cls (objc_allocateClassPair ([SuperclassType
class], getRandomisedName (nameRoot
).toUTF8(), 0))
366 auto kvoSubclassName
= String ("NSKVONotifying_") + class_getName (cls
);
368 if (objc_getClass (kvoSubclassName
.toUTF8()) == nullptr)
369 objc_disposeClassPair (cls
);
374 objc_registerClassPair (cls
);
377 SuperclassType
* createInstance() const
379 return class_createInstance (cls
, 0);
382 template <typename Type
>
383 void addIvar (const char* name
)
385 BOOL b
= class_addIvar (cls
, name
, sizeof (Type
), (uint8_t) rint (log2 (sizeof (Type
))), @
encode (Type
));
386 jassert (b
); ignoreUnused (b
);
389 template <typename Result
, typename
... Args
>
390 void addMethod (SEL selector
, Result (*callbackFn
) (id
, SEL
, Args
...))
392 const auto s
= detail::makeCompileTimeStr (@
encode (Result
), @
encode (id
), @
encode (SEL
), @
encode (Args
)...);
393 const auto b
= class_addMethod (cls
, selector
, (IMP
) callbackFn
, s
.data());
397 void addProtocol (Protocol
* protocol
)
399 BOOL b
= class_addProtocol (cls
, protocol
);
400 jassert (b
); ignoreUnused (b
);
403 template <typename ReturnType
, typename
... Params
>
404 static ReturnType
sendSuperclassMessage (id self
, SEL sel
, Params
... params
)
406 return ObjCMsgSendSuper
<SuperclassType
, ReturnType
, Params
...> (self
, sel
, params
...);
412 static String
getRandomisedName (const char* root
)
414 return root
+ String::toHexString (juce::Random::getSystemRandom().nextInt64());
417 JUCE_DECLARE_NON_COPYABLE (ObjCClass
)
420 //==============================================================================
422 template <class JuceClass
>
423 struct ObjCLifetimeManagedClass
: public ObjCClass
<NSObject
>
425 ObjCLifetimeManagedClass()
426 : ObjCClass
<NSObject
> ("ObjCLifetimeManagedClass_")
428 addIvar
<JuceClass
*> ("cppObject");
430 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
431 addMethod (@
selector (initWithJuceObject
:), initWithJuceObject
);
432 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
434 addMethod (@
selector (dealloc
), dealloc
);
439 static id
initWithJuceObject (id _self
, SEL
, JuceClass
* obj
)
441 NSObject
* self
= sendSuperclassMessage
<NSObject
*> (_self
, @
selector (init
));
442 object_setInstanceVariable (self
, "cppObject", obj
);
447 static void dealloc (id _self
, SEL
)
449 if (auto* obj
= getIvar
<JuceClass
*> (_self
, "cppObject"))
452 object_setInstanceVariable (_self
, "cppObject", nullptr);
455 sendSuperclassMessage
<void> (_self
, @
selector (dealloc
));
458 static ObjCLifetimeManagedClass objCLifetimeManagedClass
;
461 template <typename Class
>
462 ObjCLifetimeManagedClass
<Class
> ObjCLifetimeManagedClass
<Class
>::objCLifetimeManagedClass
;
465 // this will return an NSObject which takes ownership of the JUCE instance passed-in
466 // This is useful to tie the life-time of a juce instance to the life-time of an NSObject
467 template <typename Class
>
468 NSObject
* createNSObjectFromJuceClass (Class
* obj
)
470 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wobjc-method-access")
471 return [ObjCLifetimeManagedClass
<Class
>::objCLifetimeManagedClass
.createInstance() initWithJuceObject
:obj
];
472 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
475 // Get the JUCE class instance that was tied to the life-time of an NSObject with the
477 template <typename Class
>
478 Class
* getJuceClassFromNSObject (NSObject
* obj
)
480 return obj
!= nullptr ? getIvar
<Class
*> (obj
, "cppObject") : nullptr;
483 template <typename ReturnT
, class Class
, typename
... Params
>
484 ReturnT (^CreateObjCBlock(Class
* object
, ReturnT (Class::*fn
)(Params
...))) (Params
...)
486 __block Class
* _this
= object
;
487 __block
ReturnT (Class::*_fn
)(Params
...) = fn
;
489 return [[^ReturnT (Params
... params
) { return (_this
->*_fn
) (params
...); } copy
] autorelease
];
492 template <typename BlockType
>
496 ObjCBlock() { block
= nullptr; }
497 template <typename R
, class C
, typename
... P
>
498 ObjCBlock (C
* _this
, R (C::*fn
)(P
...)) : block (CreateObjCBlock (_this
, fn
)) {}
499 ObjCBlock (BlockType b
) : block ([b copy
]) {}
500 ObjCBlock
& operator= (const BlockType
& other
) { if (block
!= nullptr) { [block release
]; } block
= [other copy
]; return *this; }
501 bool operator== (const void* ptr
) const { return ((const void*) block
== ptr
); }
502 bool operator!= (const void* ptr
) const { return ((const void*) block
!= ptr
); }
503 ~ObjCBlock() { if (block
!= nullptr) [block release
]; }
505 operator BlockType() const { return block
; }