diff --git a/openVulkanoCpp/Base/Event.cpp b/openVulkanoCpp/Base/Event.cpp new file mode 100644 index 0000000..e2358a6 --- /dev/null +++ b/openVulkanoCpp/Base/Event.cpp @@ -0,0 +1,12 @@ +/* + * 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 "Event.hpp" + +namespace openVulkanoCpp +{ + std::vector INamedEventProcessor::processors; +} \ No newline at end of file diff --git a/openVulkanoCpp/Base/Event.hpp b/openVulkanoCpp/Base/Event.hpp index 2407253..e4df9cf 100644 --- a/openVulkanoCpp/Base/Event.hpp +++ b/openVulkanoCpp/Base/Event.hpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include namespace openVulkanoCpp @@ -21,6 +23,34 @@ namespace openVulkanoCpp virtual void SetInvalid() = 0; }; + class INamedEventProcessor + { + static std::vector processors; + + public: + // Keep this in sync with all implementations! synctag: NamedEventProcessor_Parameters_SYNC_TAG + typedef std::variant Parameters; + + static bool HasProcessor() { return !processors.empty(); } + + static void NotifyAll(const std::string& eventName, const std::vector& parameters) + { + for(const auto& processor : processors) + { + processor->Notify(eventName, parameters); + } + } + + INamedEventProcessor() { processors.push_back(this); } + + virtual ~INamedEventProcessor() + { + processors.erase(std::remove_if(processors.begin(), processors.end(), [this](auto v) { return v==this; }), processors.end()); + } + + virtual void Notify(const std::string& eventName, const std::vector& parameters) const = 0; + }; + template class Event final { @@ -29,7 +59,7 @@ namespace openVulkanoCpp private: - enum class EventHandlerType { STATIC, INSTANCED, INSTANCED_SHARED_PTR, INSTANCED_WEAK_PTR, FUNCTIONAL }; + enum class EventHandlerType { STATIC, INSTANCED, INSTANCED_SHARED_PTR, INSTANCED_WEAK_PTR, FUNCTIONAL, NAMED }; //region Event Handlers class EventHandler : public IEventHandler @@ -184,6 +214,33 @@ namespace openVulkanoCpp private: Function function; }; + + class NamedEventHandler final : public EventHandler + { + std::string m_name; + + public: + NamedEventHandler(const std::string& name) : + EventHandler(EventHandlerType::NAMED), m_name(name) + {} + + void Notify(Arguments... args) const override + { + if (INamedEventProcessor::HasProcessor()) + { + std::vector attributes; + attributes.reserve(sizeof...(Arguments)); + (attributes.emplace_back(args), ...); + INamedEventProcessor::NotifyAll(m_name, attributes); + } + } + + protected: + bool ShouldDelete(EventHandler* other) const override + { + return false; + } + }; //endregion std::vector> handlers; @@ -215,6 +272,17 @@ namespace openVulkanoCpp } public: + Event() = default; + + Event(const std::string& name) + { + RegisterName(name); + } + + Event(const Event& event) = default; + + Event(Event&& event) = default; + [[nodiscard]] bool HasHandlers() const { return !handlers.empty(); @@ -235,6 +303,11 @@ namespace openVulkanoCpp NotifyAll(std::forward(args)...); } + IEventHandler* RegisterName(const std::string& name) + { + return Add(new NamedEventHandler(name)); + } + template IEventHandler* RegisterHandler(Instance* instance, Method method) { @@ -391,4 +464,4 @@ namespace openVulkanoCpp { return std::pair, Method>(instance, method); } -} \ No newline at end of file +} diff --git a/openVulkanoCpp/Host/iOS/NamedEventProcessor.h b/openVulkanoCpp/Host/iOS/NamedEventProcessor.h new file mode 100644 index 0000000..4d47496 --- /dev/null +++ b/openVulkanoCpp/Host/iOS/NamedEventProcessor.h @@ -0,0 +1,23 @@ +/* + * 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/. + */ + +#pragma once + +#include "Base/Event.hpp" + +namespace openVulkanoCpp +{ + class NamedEventProcessor final : public INamedEventProcessor + { + std::vector m_classNamePrefixes; + public: + NamedEventProcessor() { RegisterClassNamePrefix(""); } + + void RegisterClassNamePrefix(const std::string& prefix) { m_classNamePrefixes.push_back(prefix); } + + void Notify(const std::string& eventName, const std::vector& parameters) const override; + }; +} \ No newline at end of file diff --git a/openVulkanoCpp/Host/iOS/NamedEventProcessor.mm b/openVulkanoCpp/Host/iOS/NamedEventProcessor.mm new file mode 100644 index 0000000..0fd6f64 --- /dev/null +++ b/openVulkanoCpp/Host/iOS/NamedEventProcessor.mm @@ -0,0 +1,150 @@ +/* + * 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 openVulkanoCpp +{ + 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) } + }; + + // 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& 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; + } + + 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::string& eventName) + { + for (const std::string& prefix : prefixes) + { + // look for class in all registered class prefixes + NSString* eventClassName = [NSString stringWithUTF8String:(prefix + eventName).c_str()]; + if (Class eventClass = NSClassFromString(eventClassName)) + { + return eventClass; + } + } + return nullptr; + } + } + + void NamedEventProcessor::Notify(const std::string& eventName, const std::vector& parameters) const + { + if (auto eventClass = FindGenericEventClass(m_classNamePrefixes, eventName)) + { + if (auto init = GetInitForClass(eventClass, BuildTypeName(parameters))) + { + auto 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)) + { + [[NSNotificationCenter defaultCenter] postNotificationName:NSStringFromClass(clazz) object:event userInfo:nil]; + } + } + else + { + Logger::MANAGER->error("Cannot find viable constructor for event %s", eventName.c_str()); + } + } + else + { + Logger::MANAGER->debug("Cannot find implementation for event %s", eventName); + } + } +}