summaryrefslogtreecommitdiffstats
path: root/src/Event.hpp
blob: 752fda432180235c281972edcf61dc03313049fa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#pragma once

#include <typeinfo>
#include <memory>
#include <functional>
#include <queue>
#include <map>
#include <list>
#include <mutex>


size_t constexpr StrHash(char const *input) {
	return *input ? static_cast<size_t>(*input) + 33 * StrHash(input + 1) : 5381;
}

class Event {
	struct EventDataBase {
		virtual ~EventDataBase() {}
		virtual const std::type_info& Type() const = 0;
	};

	template<typename T>
	struct EventData : EventDataBase {
		EventData(const T &val) : data(val) {}

		const std::type_info& Type() const override {
			return typeid(data);
		}

		T data;
	};

	std::shared_ptr<EventDataBase> data;

public:
	const size_t id;

	template<typename T>
	Event(size_t eventId, const T &value) :
		id(eventId),
		data(std::make_shared<EventData<T>>(value)) {}

	~Event() = default;

	Event(const Event &other) = default;

	Event &operator=(const Event &) = default;

	Event(Event &&) = delete;

	Event &operator=(Event &&) = delete;

	template<typename T>
	const T& get() const {
		if (typeid(T) != data->Type())
			throw std::runtime_error(std::string("Type ") + typeid(T).name() + " encountered but " + data->Type().name() + " expected");
		return static_cast<EventData<T>*>(data.get())->data;
	}
};

class EventListener {
	friend class EventSystem;
	using HandlerType = std::function<void(const Event&)>;
	std::queue<Event> events;
	std::map<size_t, HandlerType> handlers;
	std::mutex eventsQueueMutex;
	std::mutex handlersMutex;
public:
	EventListener();

	~EventListener();

	void HandleEvent();

	void HandleAllEvents();

	bool NotEmpty();

	void WaitEvent();

	void RegisterHandler(size_t eventId, const HandlerType &data) {
		std::lock_guard<std::mutex> lock(handlersMutex);
		handlers[eventId] = data;
	}

	void RegisterHandler(const char *eventId, const HandlerType & data) {
		RegisterHandler(StrHash(eventId), data);
	}
};

class EventSystem {
	friend class EventListener;
	static std::list<EventListener*> listeners;
	static std::mutex listenersMutex;

public:
	template <typename T>
	static void PushEvent(size_t eventId, T data) {
		Event event(eventId, data);

		std::lock_guard<std::mutex> listenersLock(listenersMutex);
		for (auto& listener : listeners) {
			std::lock_guard<std::mutex> lock(listener->eventsQueueMutex);
			std::lock_guard<std::mutex> lockHandlers(listener->handlersMutex);
			auto it = listener->handlers.find(eventId);
			if (it == listener->handlers.end())
				continue;			

			listener->events.push(event);
		}
	}

	template <typename T>
	static void DirectEventCall(size_t eventId, T data) {
		Event event(eventId, data);

		std::lock_guard<std::mutex> listenersLock(listenersMutex);
		for (auto & listener : listeners) {
			std::lock_guard<std::mutex> lock(listener->eventsQueueMutex);
			std::lock_guard<std::mutex> lockHandlers(listener->handlersMutex);
			auto it = listener->handlers.find(eventId);
			if (it == listener->handlers.end())
				continue;			

			it->second(event);
		}
	}
};

#define PUSH_EVENT(eventName, data) EventSystem::PushEvent(StrHash(eventName),data)

#define DIRECT_EVENT_CALL(eventName,data) EventSystem::DirectEventCall(StrHash(eventName),data)