IEvents.h#

Fully qualified name: carb/events/IEvents.h

File members: carb/events/IEvents.h

// SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
//
// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
// property and proprietary rights in and to this material, related
// documentation and any modifications thereto. Any use, reproduction,
// disclosure or distribution of this material and related documentation
// without an express license agreement from NVIDIA CORPORATION or
// its affiliates is strictly prohibited.

#pragma once

#include "EventsTypes.h"

#include "../Interface.h"
#include "../InterfaceUtils.h"
#include "../RString.h"
#include "../dictionary/DictionaryUtils.h"

#include "../../omni/Vector.h"

#include <utility>
#include "../Defines.h"

#define carb_events_IEvents_latest CARB_HEXVERSION(1, 5)
#ifndef carb_events_IEvents
#    define carb_events_IEvents CARB_HEXVERSION(1, 4)
#endif

#ifndef CARB_EVENTS_DEPRECATIONS
#    define CARB_EVENTS_DEPRECATIONS 1
#endif

#if CARB_EVENTS_DEPRECATIONS && CARB_VERSION_ATLEAST(carb_events_IEvents, 1, 5)
#    define CARB_EVENTS_DEPRECATED_1_5(msg) CARB_DEPRECATED(msg)
#else
#    define CARB_EVENTS_DEPRECATED_1_5(msg)
#endif

namespace carb::events
{

class IEvent : public IObject
{
public:
    EventType type;
    SenderId sender;
    dictionary::Item* payload;

    template <typename ValueT>
    CARB_EVENTS_DEPRECATED_1_5("Use setValue() with string_view instead")
    void setValues(const std::pair<const char*, ValueT>& param);

    template <typename ValueT, typename... ValuesT>
    CARB_EVENTS_DEPRECATED_1_5("Use setValue() with string_view instead")
    void setValues(const std::pair<const char*, ValueT>& param, ValuesT&&... params);

    virtual void consume() = 0;

    CARB_EVENTS_DEPRECATED_1_5("Use attachObject with cpp::string_view")
    virtual void internalAttachObject(const char* name, carb::IObject* ptr) = 0;

    CARB_EVENTS_DEPRECATED_1_5("Use retrieveObject() with string_view instead")
    carb::ObjectPtr<carb::IObject> retrieveObject(cpp::unbounded_string name)
    {
        // cannot pass ObjectPtr by value across ABI boundaries
        carb::ObjectPtr<carb::IObject> ptr{};
        CARB_IGNORE_DEPRECATION_BEGIN
        internalRetrieveObject(name, ptr);
        CARB_IGNORE_DEPRECATION_END
        return ptr;
    }

    CARB_EVENTS_DEPRECATED_1_5("Use retrieveObject with cpp::string_view")
    virtual void internalRetrieveObject(const char* name, carb::ObjectPtr<carb::IObject>& out) = 0;

#if CARB_VERSION_ATLEAST(carb_events_IEvents, 1, 4)
    virtual void getObjectNames(omni::vector<RString>& out) const = 0;
#endif

#if CARB_VERSION_ATLEAST(carb_events_IEvents, 1, 5)
    template <typename ValueT>
    void setValue(cpp::string_view name, ValueT&& value);

    virtual void attachObject(carb::cpp::string_view name, carb::IObject* ptr) = 0;

    CARB_EVENTS_DEPRECATED_1_5("Use attachObject() with string_view instead")
    void attachObject(cpp::unbounded_string name, carb::IObject* ptr)
    {
        CARB_IGNORE_DEPRECATION_BEGIN
        internalAttachObject(name, ptr);
        CARB_IGNORE_DEPRECATION_END
    }

    carb::ObjectPtr<carb::IObject> retrieveObject(carb::cpp::string_view name)
    {
        // cannot pass ObjectPtr by value across ABI boundaries
        carb::ObjectPtr<carb::IObject> ptr{};
        internalRetrieveObject(name, ptr);
        return ptr;
    }

    virtual void internalRetrieveObject(carb::cpp::string_view name, carb::ObjectPtr<carb::IObject>& out) = 0;

    virtual bool isConsumed() const noexcept = 0;
#endif
};

using IEventPtr = ObjectPtr<IEvent>;

class ISubscription : public IObject
{
public:
    virtual void unsubscribe() = 0;

    virtual cpp::zstring_view getNameS() const noexcept = 0;

    CARB_EVENTS_DEPRECATED_1_5("Use getNameS() instead")
    virtual const char* getName() const noexcept = 0;

#if CARB_VERSION_ATLEAST(carb_events_IEvents, 1, 3)
    virtual Order getOrder() const noexcept = 0;

    virtual void setOrder(Order order) noexcept = 0;

    virtual bool isEnabled() const noexcept = 0;

    virtual void setEnabled(bool enabled) noexcept = 0;
#endif
};

using ISubscriptionPtr = ObjectPtr<ISubscription>;

class IEventStream1_0 : public IObject
{
public:
    virtual IEvent* createEventPtr(EventType eventType, SenderId sender) = 0;

    template <typename... ValuesT>
    ObjectPtr<IEvent> createEvent(EventType eventType, SenderId sender, ValuesT&&... values);

    virtual void dispatch(IEvent* e) = 0;

    virtual void push(IEvent* e) = 0;

    virtual void pushBlocked(IEvent* e) = 0;

    virtual size_t getCount() = 0;

    ObjectPtr<IEvent> pop();

    virtual IEvent* popPtr() = 0;

    ObjectPtr<IEvent> tryPop();

    virtual IEvent* tryPopPtr() = 0;

    void pump();

    CARB_EVENTS_DEPRECATED_1_5("Use createSubscriptionToPop() with string_view instead")
    ObjectPtr<ISubscription> createSubscriptionToPop(IEventListener* listener,
                                                     Order order,
                                                     const char* subscriptionName = nullptr)
    {
        return stealObject(this->internalCreateSubscriptionToPopPtr(listener, order, subscriptionName));
    }
    CARB_EVENTS_DEPRECATED_1_5("Use createSubscriptionToPopByType() with string_view instead")
    ObjectPtr<ISubscription> createSubscriptionToPopByType(EventType eventType,
                                                           IEventListener* listener,
                                                           Order order,
                                                           const char* subscriptionName = nullptr)
    {
        return stealObject(this->internalCreateSubscriptionToPopByTypePtr(eventType, listener, order, subscriptionName));
    }
    virtual ISubscription* internalCreateSubscriptionToPopPtr(IEventListener* listener,
                                                              Order order,
                                                              const char* subscriptionName) = 0;

    CARB_EVENTS_DEPRECATED_1_5("Use createSubscriptionToPop() with string_view instead")
    ISubscription* createSubscriptionToPopPtr(IEventListener* listener, Order order, const char* subscriptionName = nullptr)
    {
        return internalCreateSubscriptionToPopPtr(listener, order, subscriptionName);
    }
    virtual ISubscription* internalCreateSubscriptionToPopByTypePtr(EventType eventType,
                                                                    IEventListener* listener,
                                                                    Order order,
                                                                    const char* subscriptionName) = 0;
    CARB_EVENTS_DEPRECATED_1_5("Use createSubscriptionToPop() with string_view instead")
    ISubscription* createSubscriptionToPopByTypePtr(EventType eventType,
                                                    IEventListener* listener,
                                                    Order order,
                                                    const char* subscriptionName = nullptr)
    {
        return internalCreateSubscriptionToPopByTypePtr(eventType, listener, order, subscriptionName);
    }

    CARB_EVENTS_DEPRECATED_1_5("Use createSubscriptionToPush() with string_view instead")
    ObjectPtr<ISubscription> createSubscriptionToPush(IEventListener* listener,
                                                      Order order,
                                                      const char* subscriptionName = nullptr)
    {
        return stealObject(this->internalCreateSubscriptionToPushPtr(listener, order, subscriptionName));
    }

    CARB_EVENTS_DEPRECATED_1_5("Use createSubscriptionToPushByType() with string_view instead")
    ObjectPtr<ISubscription> createSubscriptionToPushByType(EventType eventType,
                                                            IEventListener* listener,
                                                            Order order,
                                                            const char* subscriptionName = nullptr)
    {
        return stealObject(this->internalCreateSubscriptionToPushByTypePtr(eventType, listener, order, subscriptionName));
    }

    CARB_EVENTS_DEPRECATED_1_5("Use createSubscriptionToPushByTypePtr() with string_view instead")
    ISubscription* createSubscriptionToPushByTypePtr(EventType eventType,
                                                     IEventListener* listener,
                                                     Order order,
                                                     const char* subscriptionName = nullptr)
    {
        return internalCreateSubscriptionToPushByTypePtr(eventType, listener, order, subscriptionName);
    }
    virtual ISubscription* internalCreateSubscriptionToPushPtr(IEventListener* listener,
                                                               Order order,
                                                               const char* subscriptionName) = 0;

    CARB_EVENTS_DEPRECATED_1_5("Use createSubscriptionToPush() with string_view instead")
    ISubscription* createSubscriptionToPushPtr(IEventListener* listener, Order order, const char* subscriptionName = nullptr)
    {
        return internalCreateSubscriptionToPushPtr(listener, order, subscriptionName);
    }
    virtual ISubscription* internalCreateSubscriptionToPushByTypePtr(EventType eventType,
                                                                     IEventListener* listener,
                                                                     Order order,
                                                                     const char* subscriptionName) = 0;

    virtual bool internalSetSubscriptionToPopOrder(const char* subscriptionName, Order order) = 0;

    CARB_EVENTS_DEPRECATED_1_5("Use setSubscriptionToPopOrder() with string_view instead")
    bool setSubscriptionToPopOrder(const char* subscriptionName, Order order)
    {
        return internalSetSubscriptionToPopOrder(subscriptionName, order);
    }

    virtual bool internalSetSubscriptionToPushOrder(const char* subscriptionName, Order order) = 0;

    CARB_EVENTS_DEPRECATED_1_5("Use setSubscriptionToPushOrder() with string_view instead")
    bool setSubscriptionToPushOrder(const char* subscriptionName, Order order)
    {
        return internalSetSubscriptionToPushOrder(subscriptionName, order);
    }

    virtual bool internalGetSubscriptionToPopOrder(const char* subscriptionName, Order* order) = 0;

    CARB_EVENTS_DEPRECATED_1_5("Use getSubscriptionToPopOrder() with string_view instead")
    bool getSubscriptionToPopOrder(const char* subscriptionName, Order* order)
    {
        return internalGetSubscriptionToPopOrder(subscriptionName, order);
    }

    virtual bool internalGetSubscriptionToPushOrder(const char* subscriptionName, Order* order) = 0;

    CARB_EVENTS_DEPRECATED_1_5("Use getSubscriptionToPushOrder() with string_view instead")
    bool getSubscriptionToPushOrder(const char* subscriptionName, Order* order)
    {
        return internalGetSubscriptionToPushOrder(subscriptionName, order);
    }

    CARB_EVENTS_DEPRECATED_1_5("Use getNameS() instead")
    virtual const char* getName() const noexcept = 0;

    //                                                    Helpers                                                     //

    template <typename... ValuesT>
    IEvent* createEventPtr(EventType eventType, SenderId sender, ValuesT&&... values);

    void pushWithSender(EventType eventType, SenderId sender);

    template <typename... ValuesT>
    void pushWithSender(EventType eventType, SenderId sender, ValuesT&&... values);

    template <typename... ValuesT>
    void push(EventType eventType, ValuesT&&... values);

    void pushBlockedWithSender(EventType eventType, SenderId sender);

    template <typename... ValuesT>
    void pushBlockedWithSender(EventType eventType, SenderId sender, ValuesT&&... values);

    template <typename... ValuesT>
    void pushBlocked(EventType eventType, ValuesT&&... values);

    template <typename... ValuesT>
    void dispatch(EventType eventType, SenderId sender, ValuesT&&... values);

    void dispatch(EventType eventType, SenderId sender);
};

// Apparently doxygen ignores @private on macros and complains that it's not documented.
#define CARBLOCAL_IEVENTSTREAM IEventStream1_0

#if CARB_VERSION_ATLEAST(carb_events_IEvents, 1, 3)
class IEventStream1_3 : public IEventStream1_0
{
public:
    virtual size_t getSubscriptionToPopCount() const = 0;
    virtual size_t getSubscriptionToPushCount() const = 0;

    virtual bool walkSubscriptionsToPop(SubscriptionCallback callback, void* userData) const = 0;

    virtual bool walkSubscriptionsToPush(SubscriptionCallback callback, void* userData) const = 0;
};

#    undef CARBLOCAL_IEVENTSTREAM
#    define CARBLOCAL_IEVENTSTREAM IEventStream1_3
#endif

#if CARB_VERSION_ATLEAST(carb_events_IEvents, 1, 5)
class IEventStream1_5 : public IEventStream1_3
{
    using BaseClass = IEventStream1_3;

public:
    ObjectPtr<ISubscription> createSubscriptionToPop(IEventListener* listener,
                                                     Order order,
                                                     cpp::string_view subscriptionName);

    ObjectPtr<ISubscription> createSubscriptionToPop(IEventListener* listener);

    ObjectPtr<ISubscription> createSubscriptionToPopByType(EventType eventType,
                                                           IEventListener* listener,
                                                           Order order,
                                                           cpp::string_view subscriptionName);

    ObjectPtr<ISubscription> createSubscriptionToPopByType(EventType eventType, IEventListener* listener);

    virtual ISubscription* createSubscriptionToPopPtr(IEventListener* listener,
                                                      Order order,
                                                      cpp::string_view subscriptionName) = 0;

    ISubscription* createSubscriptionToPopPtr(IEventListener* listener)
    {
        return createSubscriptionToPopPtr(listener, kDefaultOrder, {});
    }

    virtual ISubscription* createSubscriptionToPopByTypePtr(EventType eventType,
                                                            IEventListener* listener,
                                                            Order order,
                                                            cpp::string_view subscriptionName) = 0;

    ISubscription* createSubscriptionToPopByTypePtr(EventType eventType, IEventListener* listener)
    {
        return createSubscriptionToPopByTypePtr(eventType, listener, kDefaultOrder, {});
    }

    ObjectPtr<ISubscription> createSubscriptionToPush(IEventListener* listener,
                                                      Order order,
                                                      cpp::string_view subscriptionName);

    ObjectPtr<ISubscription> createSubscriptionToPush(IEventListener* listener);

    ObjectPtr<ISubscription> createSubscriptionToPushByType(EventType eventType,
                                                            IEventListener* listener,
                                                            Order order,
                                                            cpp::string_view subscriptionName);

    ObjectPtr<ISubscription> createSubscriptionToPushByType(EventType eventType, IEventListener* listener);

    virtual ISubscription* createSubscriptionToPushPtr(IEventListener* listener,
                                                       Order order,
                                                       cpp::string_view subscriptionName) = 0;

    ISubscription* createSubscriptionToPushPtr(IEventListener* listener)
    {
        return createSubscriptionToPushPtr(listener, kDefaultOrder, {});
    }

    virtual ISubscription* createSubscriptionToPushByTypePtr(EventType eventType,
                                                             IEventListener* listener,
                                                             Order order,
                                                             cpp::string_view subscriptionName) = 0;

    ISubscription* createSubscriptionToPushByTypePtr(EventType eventType, IEventListener* listener)
    {
        return createSubscriptionToPushByTypePtr(eventType, listener, kDefaultOrder, {});
    }

    virtual bool setSubscriptionToPopOrder(cpp::string_view subscriptionName, Order order) = 0;

    virtual bool setSubscriptionToPushOrder(cpp::string_view subscriptionName, Order order) = 0;

    virtual bool getSubscriptionToPopOrder(cpp::string_view subscriptionName, Order* order) = 0;

    virtual bool getSubscriptionToPushOrder(cpp::string_view subscriptionName, Order* order) = 0;

    virtual cpp::zstring_view getNameS() const noexcept = 0;
};

#    undef CARBLOCAL_IEVENTSTREAM
#    define CARBLOCAL_IEVENTSTREAM IEventStream1_5
#endif

class IEventStream : public CARBLOCAL_IEVENTSTREAM
{
    // Implementation note: MSVC builds its vtables grouping functions by name, not in the order they're listed in the
    // class definition. So that means if we add functions to the end for minor versions, they can end up scattered
    // throughout the vtable, and we get ABI breaks. That's why the class is inherited like so.
};

using IEventStreamPtr = ObjectPtr<IEventStream>;

struct IEvents
{
    // 1.0 - Initial release
    // 1.1 - added internalCreateEventStream
    // 1.2 - added internalAttachObject() and retrieveObject() to IEvent
    // 1.3 - API additions for more control over subscriptions
    // 1.4 - added getObjectNames() to IEvent
    CARB_PLUGIN_INTERFACE_EX("carb::events::IEvents", carb_events_IEvents_latest, carb_events_IEvents)

    CARB_DEPRECATED("Use internalCreateEventStream instead") virtual IEventStream* createEventStreamPtr() = 0;

    virtual SenderId acquireUniqueSenderId() = 0;

    virtual void releaseUniqueSenderId(SenderId senderId) = 0;

    virtual IEventStream* internalCreateEventStream(const char* name) noexcept = 0;

#if CARB_VERSION_ATLEAST(carb_events_IEvents, 1, 5)
    virtual IEventStream* internalCreateEventStreamS(cpp::string_view name) noexcept = 0;

    IEventStreamPtr createEventStream(cpp::string_view name = {}) noexcept
    {
        return stealObject(internalCreateEventStreamS(name));
    }

    CARB_EVENTS_DEPRECATED_1_5("Use createEventStream(string_view)")
    IEventStreamPtr createEventStream(cpp::unbounded_string name) noexcept
    {
        return stealObject(internalCreateEventStream(name));
    }
#else
    IEventStreamPtr createEventStream(const char* name = nullptr) noexcept
    {
        return stealObject(internalCreateEventStream(name));
    }
#endif // CARB_VERSION_ATLEAST(carb_events_IEvents, 1, 5)
};

//                                                Inline Functions                                                    //

template <typename ValueT>
inline void IEvent::setValues(const std::pair<const char*, ValueT>& param)
{
    CARB_IGNORE_DEPRECATION_BEGIN
    if constexpr (cpp::is_unbounded_character_sequence_v<ValueT, char>)
        dictionary::detail::makeAtPath(getCachedInterface<dictionary::IDictionary>(), this->payload,
                                       cpp::string_view(cpp::unsafe_length, param.first),
                                       cpp::string_view(cpp::unsafe_length, param.second));
    else
        dictionary::detail::makeAtPath(getCachedInterface<dictionary::IDictionary>(), this->payload,
                                       cpp::string_view(cpp::unsafe_length, param.first), param.second);
    CARB_IGNORE_DEPRECATION_END
}

template <typename ValueT, typename... ValuesT>
inline void IEvent::setValues(const std::pair<const char*, ValueT>& param, ValuesT&&... params)
{
    CARB_IGNORE_DEPRECATION_BEGIN
    this->setValues<ValueT>(param);
    this->setValues(std::forward<ValuesT>(params)...);
    CARB_IGNORE_DEPRECATION_END
}

#if CARB_VERSION_ATLEAST(carb_events_IEvents, 1, 5)
template <typename ValueT>
inline void IEvent::setValue(cpp::string_view name, ValueT&& value)
{
    auto dict = getCachedInterface<dictionary::IDictionary>();
    dictionary::detail::makeAtPath(dict, this->payload, name, std::forward<ValueT>(value));
}
#endif

template <typename... ValuesT>
inline IEvent* IEventStream1_0::createEventPtr(EventType type, SenderId sender, ValuesT&&... values)
{
    IEvent* e = createEventPtr(type, sender);
    CARB_IGNORE_DEPRECATION_BEGIN
    e->setValues(std::forward<ValuesT>(values)...);
    CARB_IGNORE_DEPRECATION_END
    return e;
}

// Removed deprecated inline implementations - now handled by templates

#if CARB_VERSION_ATLEAST(carb_events_IEvents, 1, 5)
// String view versions of subscription functions

inline ObjectPtr<ISubscription> IEventStream1_5::createSubscriptionToPop(IEventListener* listener,
                                                                         Order order,
                                                                         cpp::string_view name)
{
    return stealObject(this->createSubscriptionToPopPtr(listener, order, name));
}

inline ObjectPtr<ISubscription> IEventStream1_5::createSubscriptionToPop(IEventListener* listener)
{
    return stealObject(this->createSubscriptionToPopPtr(listener));
}

inline ObjectPtr<ISubscription> IEventStream1_5::createSubscriptionToPopByType(EventType eventType,
                                                                               IEventListener* listener,
                                                                               Order order,
                                                                               cpp::string_view name)
{
    return stealObject(this->createSubscriptionToPopByTypePtr(eventType, listener, order, name));
}

inline ObjectPtr<ISubscription> IEventStream1_5::createSubscriptionToPopByType(EventType eventType,
                                                                               IEventListener* listener)
{
    return stealObject(this->createSubscriptionToPopByTypePtr(eventType, listener));
}

inline ObjectPtr<ISubscription> IEventStream1_5::createSubscriptionToPush(IEventListener* listener,
                                                                          Order order,
                                                                          cpp::string_view name)
{
    return stealObject(this->createSubscriptionToPushPtr(listener, order, name));
}

inline ObjectPtr<ISubscription> IEventStream1_5::createSubscriptionToPush(IEventListener* listener)
{
    return stealObject(this->createSubscriptionToPushPtr(listener));
}

inline ObjectPtr<ISubscription> IEventStream1_5::createSubscriptionToPushByType(EventType eventType,
                                                                                IEventListener* listener,
                                                                                Order order,
                                                                                cpp::string_view name)
{
    return stealObject(this->createSubscriptionToPushByTypePtr(eventType, listener, order, name));
}

inline ObjectPtr<ISubscription> IEventStream1_5::createSubscriptionToPushByType(EventType eventType,
                                                                                IEventListener* listener)
{
    return stealObject(this->createSubscriptionToPushByTypePtr(eventType, listener));
}
#endif // CARB_VERSION_ATLEAST(carb_events_IEvents, 1, 5)

template <typename... ValuesT>
inline void IEventStream1_0::pushWithSender(EventType type, SenderId sender, ValuesT&&... values)
{
    IEvent* e = createEventPtr(type, sender, std::forward<ValuesT>(values)...);
    push(e);
    e->release();
}

inline void IEventStream1_0::pushWithSender(EventType type, SenderId sender)
{
    IEvent* e = createEventPtr(type, sender);
    push(e);
    e->release();
}

template <typename... ValuesT>
inline ObjectPtr<IEvent> IEventStream1_0::createEvent(EventType eventType, SenderId sender, ValuesT&&... values)
{
    return carb::stealObject(this->createEventPtr(eventType, sender, std::forward<ValuesT>(values)...));
}

template <typename... ValuesT>
inline void IEventStream1_0::push(EventType type, ValuesT&&... values)
{
    return pushWithSender(type, kGlobalSenderId, std::forward<ValuesT>(values)...);
}

template <typename... ValuesT>
inline void IEventStream1_0::pushBlockedWithSender(EventType type, SenderId sender, ValuesT&&... values)
{
    IEvent* e = createEventPtr(type, sender, std::forward<ValuesT>(values)...);
    pushBlocked(e);
    e->release();
}

inline void IEventStream1_0::pushBlockedWithSender(EventType type, SenderId sender)
{
    IEvent* e = createEventPtr(type, sender);
    pushBlocked(e);
    e->release();
}

template <typename... ValuesT>
inline void IEventStream1_0::pushBlocked(EventType type, ValuesT&&... values)
{
    return pushBlockedWithSender(type, kGlobalSenderId, std::forward<ValuesT>(values)...);
}

template <typename... ValuesT>
inline void IEventStream1_0::dispatch(EventType type, SenderId sender, ValuesT&&... values)
{
    IEvent* e = createEventPtr(type, sender, std::forward<ValuesT>(values)...);
    dispatch(e);
    e->release();
}

inline void IEventStream1_0::dispatch(EventType type, SenderId sender)
{
    IEvent* e = createEventPtr(type, sender);
    dispatch(e);
    e->release();
}

inline void IEventStream1_0::pump()
{
    const size_t eventCount = this->getCount();
    for (size_t i = 0; i < eventCount; i++)
    {
        IEvent* e = this->tryPopPtr();
        if (!e)
            break;
        e->release();
    }
}

inline ObjectPtr<IEvent> IEventStream1_0::pop()
{
    return carb::stealObject(this->popPtr());
}

inline ObjectPtr<IEvent> IEventStream1_0::tryPop()
{
    return carb::stealObject(this->tryPopPtr());
}

} // namespace carb::events

#undef CARBLOCAL_IEVENTSTREAM