VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_core / native / juce_mac_ObjCHelpers.h
blob55cb01a236ae9abd54c0f58fa34ebe45f4897c43
1 /*
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
8 licensing.
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
18 DISCLAIMED.
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.
28 namespace juce
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];
112 else
114 auto* valueString = juceStringToNS (valueVar.toString());
116 [dictionary setObject: valueString forKey: keyString];
121 return dictionary;
124 inline NSArray* varArrayToNSArray (const var& varToParse)
126 jassert (varToParse.isArray());
128 if (! varToParse.isArray())
129 return nil;
131 const auto* varArray = varToParse.getArray();
133 auto array = [NSMutableArray arrayWithCapacity: (NSUInteger) varArray->size()];
135 for (const auto& aVar : *varArray)
137 if (aVar.isObject())
139 auto* valueDictionary = varObjectToNSDictionary (aVar);
141 [array addObject: valueDictionary];
143 else if (aVar.isArray())
145 auto* valueArray = varArrayToNSArray (aVar);
147 [array addObject: valueArray];
149 else
151 auto* valueString = juceStringToNS (aVar.toString());
153 [array addObject: valueString];
157 return array;
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);
188 else
190 // Unsupported yet, add here!
191 jassertfalse;
194 return {};
197 #if JUCE_MAC
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()));
206 #endif
208 #if JUCE_INTEL
209 template <typename T>
210 struct NeedsStret
212 #if JUCE_32BIT
213 static constexpr auto value = sizeof (T) > 8;
214 #else
215 static constexpr auto value = sizeof (T) > 16;
216 #endif
219 template <>
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; };
227 #else
228 template <typename>
229 struct MetaSuperFn { static constexpr auto value = objc_msgSendSuper; };
230 #endif
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)
248 [object release];
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
262 public:
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)
271 : item (other.item)
273 if (item != nullptr)
274 [item retain];
277 ObjCObjectHandle& operator= (const ObjCObjectHandle& other)
279 auto copy = other;
280 swap (copy);
281 return *this;
284 ObjCObjectHandle (ObjCObjectHandle&& other) noexcept { swap (other); }
286 ObjCObjectHandle& operator= (ObjCObjectHandle&& other) noexcept
288 reset();
289 swap (other);
290 return *this;
293 // Note that this does *not* retain the argument.
294 void reset (T ptr) { *this = ObjCObjectHandle { ptr }; }
296 T get() const { return item; }
298 void reset()
300 if (item != nullptr)
301 [item release];
303 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); }
312 private:
313 void swap (ObjCObjectHandle& other) noexcept { std::swap (other.item, item); }
315 T item{};
318 //==============================================================================
319 namespace detail
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)
351 void* v = nullptr;
352 object_getInstanceVariable (self, name, &v);
353 return static_cast<Type> (v);
356 template <typename SuperclassType>
357 struct ObjCClass
359 ObjCClass (const char* nameRoot)
360 : cls (objc_allocateClassPair ([SuperclassType class], getRandomisedName (nameRoot).toUTF8(), 0))
364 ~ObjCClass()
366 auto kvoSubclassName = String ("NSKVONotifying_") + class_getName (cls);
368 if (objc_getClass (kvoSubclassName.toUTF8()) == nullptr)
369 objc_disposeClassPair (cls);
372 void registerClass()
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());
394 jassertquiet (b);
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...);
409 Class cls;
411 private:
412 static String getRandomisedName (const char* root)
414 return root + String::toHexString (juce::Random::getSystemRandom().nextInt64());
417 JUCE_DECLARE_NON_COPYABLE (ObjCClass)
420 //==============================================================================
421 #ifndef DOXYGEN
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);
436 registerClass();
439 static id initWithJuceObject (id _self, SEL, JuceClass* obj)
441 NSObject* self = sendSuperclassMessage<NSObject*> (_self, @selector (init));
442 object_setInstanceVariable (self, "cppObject", obj);
444 return self;
447 static void dealloc (id _self, SEL)
449 if (auto* obj = getIvar<JuceClass*> (_self, "cppObject"))
451 delete obj;
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;
463 #endif
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
476 // function above
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>
493 class ObjCBlock
495 public:
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; }
507 private:
508 BlockType block;
511 } // namespace juce