Files
OpenVulkano/openVulkanoCpp/Host/Apple/NamedEventProcessor.mm
2024-08-04 14:27:24 +02:00

172 lines
5.0 KiB
Plaintext

/*
* 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 <map>
#include <typeindex>
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
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<std::type_index, std::string> 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<INamedEventProcessor::Parameters>& 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<decltype(arg)>))->second;
}, parameter));
}
return typeName;
}
template <typename T>
ParameterValue CreateValue(const T& parameter)
{
ParameterValue value;
reinterpret_cast<T&>(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<std::string>& prefixes,
const std::vector<std::string>& 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>& 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);
}
}
}
}