Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions docs/pages/events_and_input.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ every handler registered for that event type before returning.

The event dispatch hub is `Sleak::EventDispatcher`
(`include/public/Events/Event.hpp`), an all-static class backed by an
`unordered_map<EventType, vector<shared_ptr<IDelegate>>>`:
`unordered_map<EventType, vector<shared_ptr<EventDelegateBase>>>`:

```cpp
template<typename T, typename EventT>
Expand All @@ -52,10 +52,16 @@ static std::string RegisterEventHandler(T* instance, void (T::*memberFunction)(c
template<typename EventT>
static std::string RegisterEventCallback(std::function<void(const EventT&)> callback);

static void UnregisterEvent(EventType type, std::string id);
static void UnregisterEvent(EventType type, const std::string& id);
static void UnregisterEvents(EventType type);
static void UnregisterAllEvents();

template<typename T, typename EventT>
static EventSubscription Subscribe(T* instance, void (T::*memberFunction)(const EventT&));

template<typename EventT>
static EventSubscription Subscribe(std::function<void(const EventT&)> callback);

template<typename EventT>
static void DispatchEvent(const EventT& event);
```
Expand Down Expand Up @@ -136,16 +142,23 @@ raised from `Window::Update()` is fully handled before the frame's scene
update begins. There is no event queue, no deferred delivery, and no
ordering guarantee between handlers beyond registration order.

`DispatchEvent` copies the handler vector before iterating it, so a handler
may register or unregister handlers while it runs without invalidating the
loop. Handlers added during dispatch do not receive the event currently
being delivered.

`RegisterEventHandler` returns a `std::string` id. Keep it and pass it to
`UnregisterEvent(type, id)` when the registering object dies. A handler
bound to a destroyed `this` is a dangling call, and the dispatcher has no
way to detect it. Unregister in the same place you destroy the object,
typically a scene's destructor or `OnDeactivate`.
A handler may register or unregister handlers while it runs. A handler
unregistered during dispatch is skipped for the rest of that dispatch, and
the list is compacted once the outermost dispatch returns. Handlers added
during dispatch do not receive the event currently being delivered.

A handler can set `e.Handled = true` (the flag is `mutable`, so this works
through the `const&`) to stop the event from reaching handlers registered
after it.

`RegisterEventHandler` returns a `std::string` id, the decimal form of a
counter. Keep it and pass it to `UnregisterEvent(type, id)` when the
registering object dies. A handler bound to a destroyed `this` is a
dangling call, and the dispatcher has no way to detect it. Unregister in
the same place you destroy the object, typically a scene's destructor or
`OnDeactivate`. Or use `Subscribe`, which returns a move-only
`EventSubscription` that unregisters itself when it is destroyed or
`Reset()`.

---

Expand Down
4 changes: 4 additions & 0 deletions include/public/Core/Application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ class ENGINE_API Application {

Timer FrameTimer;

EventSubscription m_keyPressedSub;
EventSubscription m_windowResizeSub;
EventSubscription m_windowFullScreenSub;

// Deferred resize — set in the event handler, applied at start of next frame
bool m_pendingResize = false;
uint32_t m_pendingResizeW = 0;
Expand Down
71 changes: 42 additions & 29 deletions include/public/Events/Delegate.hpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
#ifndef _DELEGATE_H_
#define _DELEGATE_H_

#include <Core/OSDef.hpp>
#include <Utility/Container/List.hpp>
#include <atomic>
#include <cstdint>
#include <functional>
#include <vector>
#include <memory>
#include <string>
#include <tuple>
#include <utility>

#include <random>
#include <sstream>
#include <Utility/Container/List.hpp>
#include <vector>

namespace Sleak {
/// Type-erased callable interface storable in a homogeneous handler list.
Expand Down Expand Up @@ -49,13 +50,41 @@ namespace Sleak {
std::tuple<Args...> storedArgs; // Store by reference using forward_as_tuple
};

/// Process-wide source of delegate handles. Exported so the engine
/// and every module built on it draw from one counter.
/// @ingroup events
struct ENGINE_API DelegateHandleCounter {
static inline std::atomic<uint64_t> next{0};
};

// Specialized delegate for Event types
/// Common base for EventDelegate<T> instantiations, so they can share a handler list.
/// @ingroup events
class EventDelegateBase : public IDelegate {
public:
virtual ~EventDelegateBase() = default;
virtual std::string GetID() = 0;
EventDelegateBase() { GenerateID(); }
virtual ~EventDelegateBase() = default;

/// Assigns a fresh process-unique ID, used to identify this delegate
/// for unregistration.
std::string GenerateID() {
handle = DelegateHandleCounter::next.fetch_add(
1, std::memory_order_relaxed) +
1;
id = std::to_string(handle);
return id;
}

std::string GetID() override { return id; }
/// Numeric form of GetID().
uint64_t GetHandle() const { return handle; }

private:
friend class EventDispatcher;

uint64_t handle = 0;
std::string id;
bool removed = false;
};

/// Delegate bound to a single EventT callback; holds a non-owning pointer
Expand All @@ -66,9 +95,7 @@ namespace Sleak {
public:
using FunctionType = std::function<void(const EventT&)>;

EventDelegate(FunctionType func) : function(func) {
GenerateID();
}
EventDelegate(FunctionType func) : function(std::move(func)) {}

void SetEvent(const EventT& event) {
eventPtr = &event;
Expand All @@ -80,29 +107,15 @@ namespace Sleak {
}
}

/// Assigns a fresh random 32-character ID, used to identify this delegate for unregistration.
std::string GenerateID() {
uuid = "";
std::string str =
"0123456789!^#%&=*?+-_/"
"[]{}()"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
std::random_device dev;
std::mt19937 rand(dev());
std::uniform_int_distribution<> dist(0,str.size() - 1);

for(int i = 0; i < 32; i++)
uuid += str[dist(rand)];

return uuid;
/// Calls the handler with event directly.
void Invoke(const EventT& event) {
if (function) function(event);
}

std::string GetID() override { return uuid; }

private:
FunctionType function;
const EventT* eventPtr = nullptr; // Store a pointer to avoid copying
std::string uuid;
const EventT* eventPtr =
nullptr; // Store a pointer to avoid copying
};

/// Fan-out list of Delegate<Args...> instances, all invoked together via Broadcast.
Expand Down
Loading
Loading