2020-12-22 18:10:01 +01:00
|
|
|
#pragma once
|
|
|
|
|
2021-01-11 21:11:03 +01:00
|
|
|
#include <hex.hpp>
|
|
|
|
|
2024-03-26 19:46:25 +01:00
|
|
|
#include <algorithm>
|
|
|
|
#include <functional>
|
2021-03-27 11:36:36 +01:00
|
|
|
#include <list>
|
2024-02-26 20:51:08 +01:00
|
|
|
#include <mutex>
|
2021-03-27 11:36:36 +01:00
|
|
|
#include <map>
|
|
|
|
#include <string_view>
|
2020-12-22 18:10:01 +01:00
|
|
|
|
2021-03-27 11:36:36 +01:00
|
|
|
#include <hex/api/imhex_api.hpp>
|
2023-05-02 20:35:30 +02:00
|
|
|
#include <hex/helpers/logger.hpp>
|
2020-12-22 18:10:01 +01:00
|
|
|
|
2023-05-02 20:35:30 +02:00
|
|
|
#include <wolv/types/type_name.hpp>
|
|
|
|
|
2024-06-27 17:10:01 +02:00
|
|
|
#define EVENT_DEF_IMPL(event_name, event_name_string, should_log, ...) \
|
|
|
|
struct event_name final : public hex::impl::Event<__VA_ARGS__> { \
|
|
|
|
constexpr static auto Id = [] { return hex::impl::EventId(event_name_string); }(); \
|
|
|
|
constexpr static auto ShouldLog = (should_log); \
|
|
|
|
explicit event_name(Callback func) noexcept : Event(std::move(func)) { } \
|
|
|
|
\
|
|
|
|
static EventManager::EventList::iterator subscribe(Event::Callback function) { return EventManager::subscribe<event_name>(std::move(function)); } \
|
|
|
|
static void subscribe(void *token, Event::Callback function) { EventManager::subscribe<event_name>(token, std::move(function)); } \
|
|
|
|
static void unsubscribe(const EventManager::EventList::iterator &token) noexcept { EventManager::unsubscribe(token); } \
|
|
|
|
static void unsubscribe(void *token) noexcept { EventManager::unsubscribe<event_name>(token); } \
|
|
|
|
static void post(auto &&...args) { EventManager::post<event_name>(std::forward<decltype(args)>(args)...); } \
|
|
|
|
}
|
2020-12-22 18:10:01 +01:00
|
|
|
|
2023-06-10 15:08:56 +02:00
|
|
|
#define EVENT_DEF(event_name, ...) EVENT_DEF_IMPL(event_name, #event_name, true, __VA_ARGS__)
|
|
|
|
#define EVENT_DEF_NO_LOG(event_name, ...) EVENT_DEF_IMPL(event_name, #event_name, false, __VA_ARGS__)
|
2023-05-02 20:35:30 +02:00
|
|
|
|
2023-08-06 21:33:15 +02:00
|
|
|
|
2021-03-27 11:36:36 +01:00
|
|
|
namespace hex {
|
2020-12-22 18:10:01 +01:00
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
namespace impl {
|
|
|
|
|
|
|
|
class EventId {
|
|
|
|
public:
|
2023-06-10 15:08:56 +02:00
|
|
|
explicit constexpr EventId(const char *eventName) {
|
2023-12-19 13:10:25 +01:00
|
|
|
m_hash = 0x811C'9DC5;
|
2024-01-30 22:00:42 +01:00
|
|
|
for (const char c : std::string_view(eventName)) {
|
2023-12-19 13:10:25 +01:00
|
|
|
m_hash = (m_hash >> 5) | (m_hash << 27);
|
|
|
|
m_hash ^= c;
|
2023-03-21 15:33:43 +01:00
|
|
|
}
|
2021-03-27 11:36:36 +01:00
|
|
|
}
|
2021-01-11 20:31:40 +01:00
|
|
|
|
2023-08-26 12:54:52 +02:00
|
|
|
constexpr bool operator==(const EventId &other) const {
|
2023-12-19 13:10:25 +01:00
|
|
|
return m_hash == other.m_hash;
|
2023-08-26 12:54:52 +02:00
|
|
|
}
|
2021-01-11 21:11:03 +01:00
|
|
|
|
2024-06-27 17:10:01 +02:00
|
|
|
constexpr auto operator<=>(const EventId &other) const {
|
|
|
|
return m_hash <=> other.m_hash;
|
|
|
|
}
|
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
private:
|
|
|
|
u32 m_hash;
|
|
|
|
};
|
2021-01-11 21:11:03 +01:00
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
struct EventBase {
|
|
|
|
EventBase() noexcept = default;
|
2024-01-30 22:00:42 +01:00
|
|
|
virtual ~EventBase() = default;
|
2023-03-21 15:33:43 +01:00
|
|
|
};
|
2020-12-22 18:10:01 +01:00
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
template<typename... Params>
|
2023-11-10 20:47:08 +01:00
|
|
|
struct Event : EventBase {
|
2023-03-21 15:33:43 +01:00
|
|
|
using Callback = std::function<void(Params...)>;
|
2021-03-27 11:36:36 +01:00
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
explicit Event(Callback func) noexcept : m_func(std::move(func)) { }
|
2021-03-27 11:36:36 +01:00
|
|
|
|
2024-06-24 22:53:45 +02:00
|
|
|
template<typename E>
|
|
|
|
void call(Params... params) const {
|
2024-02-18 02:12:57 +01:00
|
|
|
try {
|
|
|
|
m_func(params...);
|
|
|
|
} catch (const std::exception &e) {
|
2024-06-24 22:53:45 +02:00
|
|
|
log::error("An exception occurred while handling event {}: {}", wolv::type::getTypeName<E>(), e.what());
|
2024-03-01 18:21:15 +01:00
|
|
|
throw;
|
2024-02-18 02:12:57 +01:00
|
|
|
}
|
2023-03-21 15:33:43 +01:00
|
|
|
}
|
2021-03-27 11:36:36 +01:00
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
private:
|
|
|
|
Callback m_func;
|
|
|
|
};
|
2020-12-22 18:10:01 +01:00
|
|
|
|
2023-08-26 12:54:52 +02:00
|
|
|
template<typename T>
|
|
|
|
concept EventType = std::derived_from<T, EventBase>;
|
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief The EventManager allows subscribing to and posting events to different parts of the program.
|
impr: Refactor and restructure Event Manager (#2082)
### Problem description
This PR addresses issue #2013 that described a cluttered Event Manager.
This is a DX issue and should not impact the users whatsoever.
### Implementation description
The changes revolve around three main points:
1. the Event Manager (`event_manager.hpp`) was split into four
categories: GUI, Interaction, Lifecycle, and Provider, and two types:
Events, and Requests. This results in the following files:
- `events_gui.hpp`
- `events_interaction.hpp`
- `events_lifecycle.hpp`
- `events_provider.hpp`
- `requests_gui.hpp`
- `requests_interaction.hpp`
- `requests_lifecycle.hpp`
- `requests_provider.hpp`
2. Every event and request now has its own piece of documentation, with
a `@brief`, accompanied by a longer comment if needed, and gets its
`@param`s described.
3. The old `event_manager.hpp` import was removed and replaced by the
correct imports wherever needed, as to reduce spread of those files only
to where they are truly useful.
### Additional things
The commits have been split into (chrono-)logical steps:
- `feat`: split the Event Manager, and replace the imports
- `refactor`, `chore`: make various small changes to match the required
structure
- `docs`: add documentation for events and requests
Hopefully, this will help to review the PR.
*Note: Beware of very long rebuild times in between the commits, use
them sparingly! The Actions will ensure this PR builds anyways*
Closes #2013
---------
Signed-off-by: BioTheWolff <47079795+BioTheWolff@users.noreply.github.com>
Co-authored-by: Nik <werwolv98@gmail.com>
2025-01-25 16:32:07 +01:00
|
|
|
* To create a new event, use the EVENT_DEF macro. This will create a new event type with the given name and parameters.
|
|
|
|
* Events should be created in an `events_*.hpp` category file under the `events` folder, and never directly here.
|
2023-03-21 15:33:43 +01:00
|
|
|
*/
|
2020-12-22 18:10:01 +01:00
|
|
|
class EventManager {
|
|
|
|
public:
|
2024-06-27 17:10:01 +02:00
|
|
|
using EventList = std::multimap<impl::EventId, std::unique_ptr<impl::EventBase>>;
|
2023-03-21 15:33:43 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Subscribes to an event
|
|
|
|
* @tparam E Event
|
|
|
|
* @param function Function to call when the event is posted
|
|
|
|
* @return Token to unsubscribe from the event
|
|
|
|
*/
|
2023-08-26 12:54:52 +02:00
|
|
|
template<impl::EventType E>
|
2022-02-08 18:38:54 +01:00
|
|
|
static EventList::iterator subscribe(typename E::Callback function) {
|
2023-11-04 23:16:38 +01:00
|
|
|
std::scoped_lock lock(getEventMutex());
|
|
|
|
|
2023-07-26 13:50:51 +02:00
|
|
|
auto &events = getEvents();
|
2024-06-27 17:10:01 +02:00
|
|
|
return events.insert({ E::Id, std::make_unique<E>(function) });
|
2021-03-27 11:36:36 +01:00
|
|
|
}
|
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
/**
|
|
|
|
* @brief Subscribes to an event
|
|
|
|
* @tparam E Event
|
|
|
|
* @param token Unique token to register the event to. Later required to unsubscribe again
|
|
|
|
* @param function Function to call when the event is posted
|
|
|
|
*/
|
2023-08-26 12:54:52 +02:00
|
|
|
template<impl::EventType E>
|
2021-03-27 11:36:36 +01:00
|
|
|
static void subscribe(void *token, typename E::Callback function) {
|
2023-11-04 23:16:38 +01:00
|
|
|
std::scoped_lock lock(getEventMutex());
|
|
|
|
|
2024-12-14 19:15:49 +01:00
|
|
|
if (isAlreadyRegistered(token, E::Id)) {
|
|
|
|
log::fatal("The token '{}' has already registered the same event ('{}')", token, wolv::type::getTypeName<E>());
|
|
|
|
return;
|
fix: Event unsubscribe not working correcetly when using same key for multiple events (#1309)
<!--
Please provide as much information as possible about what your PR aims
to do.
PRs with no description will most likely be closed until more
information is provided.
If you're planing on changing fundamental behaviour or add big new
features, please open a GitHub Issue first before starting to work on
it.
If it's not something big and you still want to contact us about it,
feel free to do so !
-->
### Problem description
<!-- Describe the bug that you fixed/feature request that you
implemented, or link to an existing issue describing it -->
Fixed possible bug of `EventManager::unsubscribe`
`std::map` only allows unique key, but the same token can subscribe to
multiple events.
https://github.com/WerWolv/ImHex/blob/1a2a926b772f32d4f5e4c723c48e0e6e65a16b0a/lib/libimhex/include/hex/api/event.hpp#L104-L107
If the previous token has already subscribed to an event, then when
subscribing again, `getTokenStore().insert` will not do anything
(Because its type is `std::map`)
https://github.com/WerWolv/ImHex/blob/1a2a926b772f32d4f5e4c723c48e0e6e65a16b0a/lib/libimhex/include/hex/api/event.hpp#L122-L134
At this point in `unsubscribe`, the `iter` may not be able to find the
correct event and erase it
### Implementation description
<!-- Explain what you did to correct the problem -->
Change `tokenStore` to `std::multimap` instead of `std::map`, which
cannot unsubscribe multiple events correctly
### Screenshots
<!-- If your change is visual, take a screenshot showing it. Ideally,
make before/after sceenshots -->
### Additional things
<!-- Anything else you would like to say -->
2023-10-08 05:35:35 +08:00
|
|
|
}
|
|
|
|
|
2023-11-04 23:16:38 +01:00
|
|
|
getTokenStore().insert({ token, subscribe<E>(function) });
|
2021-03-27 11:36:36 +01:00
|
|
|
}
|
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
/**
|
|
|
|
* @brief Unsubscribes from an event
|
|
|
|
* @param token Token returned by subscribe
|
|
|
|
*/
|
|
|
|
static void unsubscribe(const EventList::iterator &token) noexcept {
|
2023-11-04 23:16:38 +01:00
|
|
|
std::scoped_lock lock(getEventMutex());
|
|
|
|
|
2023-07-26 13:50:51 +02:00
|
|
|
getEvents().erase(token);
|
2021-03-27 11:36:36 +01:00
|
|
|
}
|
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
/**
|
|
|
|
* @brief Unsubscribes from an event
|
|
|
|
* @tparam E Event
|
|
|
|
* @param token Token passed to subscribe
|
|
|
|
*/
|
2023-08-26 12:54:52 +02:00
|
|
|
template<impl::EventType E>
|
2021-03-27 11:36:36 +01:00
|
|
|
static void unsubscribe(void *token) noexcept {
|
2023-11-04 23:16:38 +01:00
|
|
|
std::scoped_lock lock(getEventMutex());
|
|
|
|
|
2024-12-14 19:15:49 +01:00
|
|
|
unsubscribe(token, E::Id);
|
2021-03-27 11:36:36 +01:00
|
|
|
}
|
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
/**
|
|
|
|
* @brief Posts an event to all subscribers of it
|
|
|
|
* @tparam E Event
|
|
|
|
* @param args Arguments to pass to the event
|
|
|
|
*/
|
2023-08-26 12:54:52 +02:00
|
|
|
template<impl::EventType E>
|
2024-03-01 18:21:15 +01:00
|
|
|
static void post(auto && ...args) {
|
2023-11-04 23:16:38 +01:00
|
|
|
std::scoped_lock lock(getEventMutex());
|
|
|
|
|
2024-06-27 17:10:01 +02:00
|
|
|
auto [begin, end] = getEvents().equal_range(E::Id);
|
|
|
|
for (auto it = begin; it != end; ++it) {
|
|
|
|
const auto &[id, event] = *it;
|
|
|
|
(*static_cast<E *const>(event.get())).template call<E>(std::forward<decltype(args)>(args)...);
|
2021-03-27 11:36:36 +01:00
|
|
|
}
|
2023-05-02 20:35:30 +02:00
|
|
|
|
|
|
|
#if defined (DEBUG)
|
2024-06-24 22:53:45 +02:00
|
|
|
if constexpr (E::ShouldLog)
|
2023-05-02 20:35:30 +02:00
|
|
|
log::debug("Event posted: '{}'", wolv::type::getTypeName<E>());
|
|
|
|
#endif
|
2021-03-27 11:36:36 +01:00
|
|
|
}
|
2022-01-24 20:53:17 +01:00
|
|
|
|
2023-03-21 15:33:43 +01:00
|
|
|
/**
|
|
|
|
* @brief Unsubscribe all subscribers from all events
|
|
|
|
*/
|
2022-08-03 10:45:50 +02:00
|
|
|
static void clear() noexcept {
|
2023-11-04 23:16:38 +01:00
|
|
|
std::scoped_lock lock(getEventMutex());
|
|
|
|
|
2023-07-26 13:50:51 +02:00
|
|
|
getEvents().clear();
|
|
|
|
getTokenStore().clear();
|
2022-08-03 10:45:50 +02:00
|
|
|
}
|
|
|
|
|
2021-03-27 11:36:36 +01:00
|
|
|
private:
|
fix: Event unsubscribe not working correcetly when using same key for multiple events (#1309)
<!--
Please provide as much information as possible about what your PR aims
to do.
PRs with no description will most likely be closed until more
information is provided.
If you're planing on changing fundamental behaviour or add big new
features, please open a GitHub Issue first before starting to work on
it.
If it's not something big and you still want to contact us about it,
feel free to do so !
-->
### Problem description
<!-- Describe the bug that you fixed/feature request that you
implemented, or link to an existing issue describing it -->
Fixed possible bug of `EventManager::unsubscribe`
`std::map` only allows unique key, but the same token can subscribe to
multiple events.
https://github.com/WerWolv/ImHex/blob/1a2a926b772f32d4f5e4c723c48e0e6e65a16b0a/lib/libimhex/include/hex/api/event.hpp#L104-L107
If the previous token has already subscribed to an event, then when
subscribing again, `getTokenStore().insert` will not do anything
(Because its type is `std::map`)
https://github.com/WerWolv/ImHex/blob/1a2a926b772f32d4f5e4c723c48e0e6e65a16b0a/lib/libimhex/include/hex/api/event.hpp#L122-L134
At this point in `unsubscribe`, the `iter` may not be able to find the
correct event and erase it
### Implementation description
<!-- Explain what you did to correct the problem -->
Change `tokenStore` to `std::multimap` instead of `std::map`, which
cannot unsubscribe multiple events correctly
### Screenshots
<!-- If your change is visual, take a screenshot showing it. Ideally,
make before/after sceenshots -->
### Additional things
<!-- Anything else you would like to say -->
2023-10-08 05:35:35 +08:00
|
|
|
static std::multimap<void *, EventList::iterator>& getTokenStore();
|
2023-07-26 13:50:51 +02:00
|
|
|
static EventList& getEvents();
|
2023-11-04 23:16:38 +01:00
|
|
|
static std::recursive_mutex& getEventMutex();
|
2024-12-14 19:15:49 +01:00
|
|
|
|
|
|
|
static bool isAlreadyRegistered(void *token, impl::EventId id);
|
|
|
|
static void unsubscribe(void *token, impl::EventId id);
|
2020-12-22 18:10:01 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|