158 lines
4.7 KiB
Plaintext
158 lines
4.7 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) }
|
|
};
|
|
|
|
// Keep this in sync with INamedEventProcessor! synctag: NamedEventProcessor_Parameters_SYNC_TAG
|
|
union ParameterValue
|
|
{
|
|
NSString* string;
|
|
BOOL boolean;
|
|
int integer;
|
|
float fp32;
|
|
double fp64;
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
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
|
|
{
|
|
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];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Logger::MANAGER->error("Cannot find constructor for named event %s", eventName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Logger::MANAGER->debug("Cannot find implementation for named event %s", eventName);
|
|
}
|
|
}
|
|
}
|