/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "NamedEventProcessor.h" #include "Base/Logger.hpp" #include #include #import #import namespace OpenVulkano { namespace { NamedEventProcessor appleNotificationNamedEventProcessor; constexpr int LONGEST_TYPE_NAME = std::max({ sizeof(@encode(NSString*)), sizeof(@encode(BOOL)), sizeof(@encode(int)), sizeof(@encode(float)), sizeof(@encode(double)) }); std::map TYPE_NAME_MAP = { { std::type_index(typeid(std::string)), @encode(NSString*) }, { std::type_index(typeid(bool)), @encode(BOOL) }, { std::type_index(typeid(int)), @encode(int) }, { std::type_index(typeid(float)), @encode(float) }, { std::type_index(typeid(double)), @encode(double) }, { std::type_index(typeid(UUID)), @encode(NSUUID*) } }; // Keep this in sync with INamedEventProcessor! synctag: NamedEventProcessor_Parameters_SYNC_TAG union ParameterValue { NSString* string; BOOL boolean; int integer; float fp32; double fp64; NSUUID* uuid; }; std::string BuildTypeName(const std::vector& parameters) { std::string typeName("@:"); typeName.reserve(2 + parameters.size() * (LONGEST_TYPE_NAME + 2)); for(const auto& parameter : parameters) { typeName.append(std::visit([](auto&& arg) { return TYPE_NAME_MAP.find(typeid(std::decay_t))->second; }, parameter)); } return typeName; } template ParameterValue CreateValue(const T& parameter) { ParameterValue value; reinterpret_cast(value) = parameter; return value; } ParameterValue CreateValue(const std::string& parameter) { ParameterValue value; value.string = [NSString stringWithUTF8String:parameter.c_str()]; return value; } ParameterValue CreateValue(const UUID& parameter) { ParameterValue value; auto uuidData = parameter.binary(); value.uuid = [[NSUUID alloc] initWithUUIDBytes:uuidData.data()]; return value; } std::string SelectorType(Method m) { std::string sig; for (auto i = 0u, count = method_getNumberOfArguments(m); i < count; ++i) { auto argType = method_copyArgumentType(m, i); sig += std::string(argType); free(argType); } return sig; } SEL GetInitForClass(Class c, const std::string &initType) { SEL init = nullptr; for (auto clazz = c; !init && clazz; clazz = class_getSuperclass(clazz)) { if (Method *methods = class_copyMethodList(clazz, nullptr)) { for (int i = 0; methods[i]; i++) { const std::string name = sel_getName(method_getName(methods[i])); const std::string type = SelectorType(methods[i]); if (0 == name.find("init") && type == initType) { init = method_getName(methods[i]); break; } } free(methods); } } return init; } Class FindGenericEventClass(const std::vector& prefixes, const std::vector& suffixes, const std::string& eventName) { // Searching for class with all registered prefix/suffix combinations for (const std::string& suffix : suffixes) { for (const std::string& prefix: prefixes) { NSString* eventClassName = [NSString stringWithUTF8String:(prefix + eventName + suffix).c_str()]; if (Class eventClass = NSClassFromString(eventClassName)) { return eventClass; } } } return nullptr; } } void NamedEventProcessor::Notify(const std::string& eventName, const std::vector& parameters) const { @autoreleasepool { if (Class eventClass = FindGenericEventClass(m_classNamePrefixes, m_classNameSuffixes, eventName)) { if (SEL init = GetInitForClass(eventClass, BuildTypeName(parameters))) { NSObject* event = [eventClass alloc]; auto inv = [NSInvocation invocationWithMethodSignature:[event methodSignatureForSelector:init]]; [inv setTarget:event]; [inv setSelector:init]; for (int i = 0; i < parameters.size(); i++) { auto arg = std::visit([](auto&& arg) { return CreateValue(arg); }, parameters[i]); [inv setArgument:&arg atIndex:(i + 2)]; } [inv invoke]; for (Class clazz = eventClass; clazz && clazz != NSObject.class; clazz = class_getSuperclass(clazz)) { NSString* notificationName; if (m_useClassNameAsEventName) notificationName = NSStringFromClass(clazz); else notificationName = [NSString stringWithUTF8String:eventName.c_str()]; [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:event userInfo:nil]; } [event release]; } else { Logger::MANAGER->error("Cannot find constructor for named event {}", eventName); } } else { Logger::MANAGER->debug("Cannot find implementation for named event {}", eventName); } } } }