Event Definitions
Event definitions are used to enable the Dispatcher to consume specific events. Defining an event definition can be done via the following macro:
};
#define DEFINE_EVENT(ClassName, EventType, EventCategory, EventCause, callback) \
struct ClassName: EventDefinitionBase<EventType> { \
inline static constexpr auto getEventCode() { \
return eventCode(EventCategory, EventCause); \
}; \
\
template<class Listener> \
static constexpr bool hasCallback = requires(Listener listener, const EventType& event) { \
listener.callback(event); \
}; \
\
template<class Listener> \
static constexpr auto getCallback(Listener* listener) -> decltype(auto) { \
if constexpr (hasCallback<Listener>) { \
return [listener](const Event& event) { \
listener->callback(event); \
}; \
} else { \
return []([[maybe_unused]] const Event& event) { \
}; \
} \
} \
\
inline static auto getName() -> std::string { \
return #ClassName; \
Key Event Definition
The event definition for key events are implemented as follows:
#pragma once
#include <obscura/events/event_definitions/event_definition.hxx>
#include <obscura/events/key_events.hxx>
namespace obscura {
DEFINE_EVENT(KeyPressedDefinition, KeyPressedEvent, EventCategory::KeyInput, EventCause::KeyPressed, onKeyPressed);
DEFINE_EVENT(KeyReleasedDefintion, KeyReleasedEvent, EventCategory::KeyInput, EventCause::KeyReleased, onKeyReleased);
DEFINE_EVENT(KeyRepeatedDefintion, KeyRepeatedEvent, EventCategory::KeyInput, EventCause::KeyRepeated, onKeyRepeated);
} // namespace obscura
Example
This example shows how key events can be used. The example implements a listener for key events, registers it with the dispatcher and then sends various events to the dispatcher and dispatches them.
#include <obscura/obscura.hxx>
using namespace obscura;
using namespace std::chrono_literals;
class Listener {
public:
static void onKeyPressed(const KeyPressedEvent& event) {
OBS_LOG_INFO("Key {} pressed", event.getKey());
}
static void onKeyReleased(const KeyReleasedEvent& event) {
OBS_LOG_INFO("Key {} released", event.getKey());
}
static void onKeyRepeated(const KeyRepeatedEvent& event) {
OBS_LOG_INFO("Key {} repeated", event.getKey());
}
static void onWindowClosed(const WindowEvent& /*unused*/) {
Scheduler<Dispatcher>::getInstance().stopMainLoop();
}
// Only required to suppress warnings because otherwise there is no listener that
// processes tick events
void onTick(const TickEvent& event) {
}
};
auto main() -> int {
try {
Logging::init();
auto keyPressedEvent = KeyPressedEvent { 0 };
auto keyReleasedEvent = KeyReleasedEvent { 1 };
auto keyRepeatedEvent = KeyRepeatedEvent { 42 };
auto listener = Listener {};
auto* scheduler = &Scheduler<Dispatcher>::getInstance();
scheduler->appendListener(listener);
scheduler->scheduleOnce(WindowEvent { EventCause::WindowClosed }, 1s);
scheduler->scheduleOnce(keyPressedEvent);
scheduler->scheduleOnce(keyReleasedEvent);
scheduler->scheduleOnce(keyRepeatedEvent);
scheduler->startMainLoop();
} catch (const std::exception& e) {
OBS_LOG_ERROR("Exception caught: {}", e.what());
}
return EXIT_SUCCESS;
}
Mouse Event Definition
Mouse events are emitted whenever a mouse movement or mouse click is being done. The event definition for mouse events are implemented as follows:
#pragma once
#include <obscura/events/event_definitions/event_definition.hxx>
#include <obscura/events/mouse_events.hxx>
namespace obscura {
DEFINE_EVENT(
MouseMovedDefintion,
MouseMovedEvent,
EventCategory::CoordinateInput,
EventCause::MouseMoved,
onMouseMoved);
DEFINE_EVENT(
MouseClickedDefinition,
MouseClickedEvent,
EventCategory::KeyInput,
EventCause::MouseClicked,
onMouseClicked);
} // namespace obscura
Example
This example shows how key events can be used. The example implements a listener for mouse events, registers it with the dispatcher and then sends various events to the dispatcher and dispatches them.
#include <obscura/obscura.hxx>
using namespace obscura;
using namespace std::chrono_literals;
class Listener {
public:
static void onMouseClicked(const MouseClickedEvent& event) {
OBS_LOG_INFO("Mouse key {} pressed", event.getKey());
OBS_LOG_INFO("Mouse moved to x: {}, y: {}", event.getPosition().x, event.getPosition().y);
}
static void onMouseMoved(const MouseMovedEvent& event) {
OBS_LOG_INFO("Mouse moved to x: {}, y: {}", event.getCurrentPosition().x, event.getCurrentPosition().y);
OBS_LOG_INFO("Mouse moved from x: {}, y: {}", event.getPreviousPosition().x, event.getPreviousPosition().y);
OBS_LOG_INFO(
"Mouse moved by {} horizontal pixels, vertical {} pixels",
event.getOffset().x,
event.getOffset().y);
}
static void onWindowClosed(const WindowEvent& /*unused*/) {
Scheduler<Dispatcher>::getInstance().stopMainLoop();
}
// Only required to suppress warnings because otherwise there is no listener that
// processes tick events
void onTick(const TickEvent& /*unused*/) {
}
};
auto main() -> int {
try {
Logging::init();
auto mouseClickedEvent = MouseClickedEvent { 2, 3, 42 };
auto mouseMovedEvent = MouseMovedEvent { 0, 5, 10, 25 };
auto listener = Listener {};
auto* scheduler = &Scheduler<Dispatcher>::getInstance();
scheduler->appendListener(listener);
scheduler->scheduleOnce(WindowEvent { EventCause::WindowClosed }, 1s);
scheduler->scheduleOnce(mouseClickedEvent);
scheduler->scheduleOnce(mouseMovedEvent);
scheduler->startMainLoop();
} catch (const std::exception& e) {
OBS_LOG_ERROR("Exception caught: {}", e.what());
}
return EXIT_SUCCESS;
}
Loop Event Definition
Loop events are triggered by the game loop(s). The events that are emitted are ``TickEvent``s that are either emitted for each tick or emitted at the end of the loop. Here is how loop events are implemented
#pragma once
#include <obscura/events/event_definitions/event_definition.hxx>
#include <obscura/events/loop_events.hxx>
namespace obscura {
DEFINE_EVENT(TickDefinition, TickEvent, EventCategory::Application, EventCause::LoopTicked, onTick);
DEFINE_EVENT(LoopEndDefinition, TickEvent, EventCategory::Application, EventCause::LoopStopped, onLoopStop);
} // namespace obscura
Example
#include <obscura/obscura.hxx>
using namespace obscura;
using namespace std::chrono_literals;
class Listener {
public:
void onTick(const TickEvent& event) {
OBS_LOG_INFO("The Frame took {} microseconds", event.getDurationSinceLastTick().count());
++tickCounter;
OBS_LOG_INFO("This is tick #{}", tickCounter);
}
static void onLoopStop(const TickEvent& /*unused*/) {
OBS_LOG_INFO("The loop stopped and I can clean stuff up!");
}
static void onWindowClosed(const WindowEvent& /*unused*/) {
Scheduler<Dispatcher>::getInstance().stopMainLoop();
}
private:
std::size_t tickCounter = 0;
};
auto main() -> int {
try {
Logging::init();
auto listener = Listener {};
auto* scheduler = &Scheduler<Dispatcher>::getInstance();
scheduler->appendListener(listener);
scheduler->scheduleOnce(WindowEvent { EventCause::WindowClosed }, 1s);
scheduler->startMainLoop();
} catch (const std::exception& e) {
OBS_LOG_ERROR("Exception caught: {}", e.what());
}
return EXIT_SUCCESS;
}
Window Event Definition
Window events are emitted whenever there is a change to the window. The events are defined as follows:
#pragma once
#include <obscura/events/event_definitions/event_definition.hxx>
#include <obscura/events/window_events.hxx>
namespace obscura {
DEFINE_EVENT(WindowClosedDefintion, WindowEvent, EventCategory::Window, EventCause::WindowClosed, onWindowClosed);
} // namespace obscura
Example
#include <obscura/obscura.hxx>
using namespace obscura;
using namespace std::chrono_literals;
class Listener {
public:
explicit Listener(Window<Dispatcher, HeadlessWindow>& window)
: window(&window) {
}
void onWindowClosed(const WindowEvent& /*unused*/) {
OBS_LOG_INFO("Close window!");
window->close();
OBS_LOG_INFO("Window was closed!");
Scheduler<Dispatcher>::getInstance().stopMainLoop();
}
// Only required to suppress warnings because otherwise there is no listener that
// processes tick events
void onTick(const TickEvent& /*unused*/) {
}
private:
Window<Dispatcher, HeadlessWindow>* window;
};
auto main() -> int {
try {
Logging::init();
auto* scheduler = &Scheduler<Dispatcher>::getInstance();
auto window = Window<Dispatcher, HeadlessWindow> { scheduler->getMainLoop(), WindowProperties {} };
auto listener = Listener { window };
scheduler->appendListener(listener);
scheduler->scheduleOnce(WindowEvent { EventCause::WindowClosed }, 1s);
scheduler->startMainLoop();
} catch (const std::exception& e) {
OBS_LOG_ERROR("Exception caught: {}", e.what());
}
}
Custom Event Definition
For creating a custom event that can be used with Obscura you have to implement a respective event definition and pass the definition to the Dispatcher. The following example shows how to define a custom event definition and how to use it in combination with the dispatcher:
#include <obscura/obscura.hxx>
#include <utility>
class CustomEvent: public obscura::Event {
public:
explicit CustomEvent(std::string message)
: obscura::Event(obscura::EventCategory::None, obscura::EventCause::None),
message(std::move(message)) {
}
[[nodiscard]] auto getMessage() const -> const std::string& {
return message;
}
private:
std::string message;
};
DEFINE_EVENT( // NOLINT(modernize-use-trailing-return-type)
CustomEventDefinition,
CustomEvent,
obscura::EventCategory::None,
obscura::EventCause::None,
onCustomEvent);
class CustomListener {
public:
static void onCustomEvent(const CustomEvent& event) {
OBS_LOG_INFO("{}", event.getMessage());
}
};
auto main() -> int {
try {
obscura::Logging::init();
auto event = CustomEvent("I traveled through the dispatcher!");
auto listener = CustomListener();
// All event definitions that are used in a project should be passed to the
// ConfigurableDispatcher template. Here, we only need one event definition.
auto dispatcher = obscura::ConfigurableDispatcher<CustomEventDefinition>();
dispatcher.appendListener(listener);
dispatcher.enqueue(event);
OBS_LOG_INFO("Event is in queue but not yet dispatched!");
dispatcher.dispatch();
OBS_LOG_INFO("Event was dispatched!");
} catch (const std::exception& e) {
OBS_LOG_ERROR("Exception caught: {}", e.what());
}
return EXIT_SUCCESS;
}