Skip to content

Commit

Permalink
rewrite events and inputs: make it simpler and more flexible
Browse files Browse the repository at this point in the history
  • Loading branch information
malytomas committed May 22, 2023
1 parent 30922ae commit e9efd50
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 171 deletions.
109 changes: 50 additions & 59 deletions sources/include/cage-core/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,60 +31,74 @@ namespace cage
}

template<class... Ts>
struct EventListener<bool(Ts...)> : private privat::EventLinker, private Delegate<bool(Ts...)>
struct EventListener<bool(Ts...)> : private privat::EventLinker
{
CAGE_FORCE_INLINE explicit EventListener(const std::source_location &location = std::source_location::current()) : privat::EventLinker(location)
{}

CAGE_FORCE_INLINE void attach(EventDispatcher<bool(Ts...)> &dispatcher, sint32 order = 0)
template<class Callable>
CAGE_FORCE_INLINE explicit EventListener(Callable &&callable, EventDispatcher<bool(Ts...)> &dispatcher, sint32 order = 0, const std::source_location &location = std::source_location::current()) : privat::EventLinker(location)
{
privat::EventLinker::attach(&dispatcher, order);
bind(std::move(callable));
attach(dispatcher, order);
}

using privat::EventLinker::detach;
using Delegate<bool(Ts...)>::bind;
using Delegate<bool(Ts...)>::clear;

private:
CAGE_FORCE_INLINE bool invoke(Ts... vs) const
template<class Callable> requires(std::is_invocable_r_v<bool, Callable, Ts...> || std::is_invocable_r_v<void, Callable, Ts...>)
CAGE_FORCE_INLINE void bind(Callable &&callable)
{
auto &d = (Delegate<bool(Ts...)>&)*this;
if (d)
return d(std::forward<Ts>(vs)...);
return false;
if constexpr (std::is_same_v<std::invoke_result_t<Callable, Ts...>, bool>)
{
del.b = std::move(callable);
vd = false;
}
else
{
del.v = std::move(callable);
vd = true;
}
}

using privat::EventLinker::logAllNames;
friend struct EventDispatcher<bool(Ts...)>;
};
CAGE_FORCE_INLINE void clear()
{
del.b.clear();
}

template<class... Ts>
struct EventListener<void(Ts...)> : private EventListener<bool(Ts...)>, private Delegate<void(Ts...)>
{
CAGE_FORCE_INLINE explicit EventListener(const std::source_location &location = std::source_location::current()) : EventListener<bool(Ts...)>(location)
CAGE_FORCE_INLINE void attach(EventDispatcher<bool(Ts...)> &dispatcher, sint32 order = 0)
{
EventListener<bool(Ts...)>::template bind<EventListener, &EventListener::invoke>(this);
privat::EventLinker::attach(&dispatcher, order);
}

using EventListener<bool(Ts...)>::attach;
using EventListener<bool(Ts...)>::detach;
using Delegate<void(Ts...)>::bind;
using Delegate<void(Ts...)>::clear;
using privat::EventLinker::detach;

private:
CAGE_FORCE_INLINE bool invoke(Ts... vs) const
{
auto &d = (Delegate<void(Ts...)>&)*this;
if (d)
d(std::forward<Ts>(vs)...);
return false;
if (!del.b)
return false;
if (vd)
{
(del.v)(std::forward<Ts>(vs)...);
return false;
}
else
return (del.b)(std::forward<Ts>(vs)...);
}

union Del
{
Delegate<bool(Ts...)> b;
Delegate<void(Ts...)> v;
CAGE_FORCE_INLINE Del() : b() {};
} del;
bool vd = false;

friend struct EventDispatcher<bool(Ts...)>;
};

template<class... Ts>
struct EventDispatcher<bool(Ts...)> : private EventListener<bool(Ts...)>
struct EventDispatcher<bool(Ts...)> : private privat::EventLinker
{
CAGE_FORCE_INLINE explicit EventDispatcher(const std::source_location &location = std::source_location::current()) : EventListener<bool(Ts...)>(location)
CAGE_FORCE_INLINE explicit EventDispatcher(const std::source_location &location = std::source_location::current()) : privat::EventLinker(location)
{}

bool dispatch(Ts... vs) const
Expand All @@ -100,37 +114,14 @@ namespace cage
return false;
}

template<class Callable> requires(std::is_invocable_r_v<bool, Callable, Ts...> || std::is_invocable_r_v<void, Callable, Ts...>)
[[nodiscard]] CAGE_FORCE_INLINE auto listen(Callable &&callable, sint32 order = 0)
template<class Callable>
[[nodiscard]] CAGE_FORCE_INLINE auto listen(Callable &&callable, sint32 order = 0, const std::source_location &location = std::source_location::current())
{
struct Listener : private Immovable
{
EventListener<bool(Ts...)> el;
Callable cl;

CAGE_FORCE_INLINE explicit Listener(Callable &&callable, EventDispatcher<bool(Ts...)> &disp, sint32 order) : cl(std::move(callable))
{
el.attach(disp, order);
el.template bind<Listener, &Listener::call>(this);
}

CAGE_FORCE_INLINE bool call(Ts... vs) const
{
if constexpr (std::is_same_v<decltype(cl(std::forward<Ts>(vs)...)), void>)
{
cl(std::forward<Ts>(vs)...);
return false;
}
else
return cl(std::forward<Ts>(vs)...);
}
};

return Listener(std::move(callable), *this, order);
return EventListener<bool(Ts...)>(std::move(callable), *this, order, location);
}

using EventListener<bool(Ts...)>::detach;
using EventListener<bool(Ts...)>::logAllNames;
using privat::EventLinker::detach;
using privat::EventLinker::logAllNames;

private:
friend struct EventListener<bool(Ts...)>;
Expand Down
41 changes: 10 additions & 31 deletions sources/include/cage-engine/inputs.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,37 +221,16 @@ namespace cage
void bind(InputsGeneralizer *generalizer);
};

template<InputClassEnum C, class T, class R = void>
struct InputListener : private EventListener<R(const GenericInput &)>, private Delegate<R(T)>
{
static_assert(std::is_same_v<R, void> || std::is_same_v<R, bool>);

explicit InputListener(const std::source_location &location = std::source_location::current()) : EventListener<R(const GenericInput &)>(location)
{
EventListener<R(const GenericInput &)>::template bind<InputListener, &InputListener::run>(this);
}

using EventListener<R(const GenericInput &)>::attach;
using EventListener<R(const GenericInput &)>::detach;
using Delegate<R(T)>::bind;
using Delegate<R(T)>::clear;

private:
template<class G = R, typename = std::enable_if_t<std::is_same_v<G, bool>>>
bool run(const GenericInput &in)
{
if (in.type == C && in.data.typeHash() == detail::typeHash<T>())
return (*(Delegate<R(T)> *)this)(in.data.get<T>());
return false;
}

template<class G = R, typename = std::enable_if_t<std::is_same_v<G, void>>>
void run(const GenericInput &in)
{
if (in.type == C && in.data.typeHash() == detail::typeHash<T>())
(*(Delegate<R(T)> *)this)(in.data.get<T>());
}
};
template<InputClassEnum C, class T, class Callable> requires(std::is_invocable_r_v<bool, Callable, T> || std::is_invocable_r_v<void, Callable, T>)
auto inputListener(Callable &&callable)
{
return [cl = std::move(callable)](const GenericInput &in) {
if (in.type == C)
return cl(in.data.get<T>());
if constexpr (std::is_same_v<std::invoke_result_t<Callable, T>, bool>)
return false;
};
}
}

#endif // guard_inputs_juhgf98ser4g
20 changes: 10 additions & 10 deletions sources/libengine/gui/gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ namespace cage
#undef GCHL_GENERATE

inputsListeners.attach(&inputsDispatchers);
inputsListeners.mousePress.bind<GuiImpl, &GuiImpl::mousePress>(this);
inputsListeners.mouseDoublePress.bind<GuiImpl, &GuiImpl::mouseDoublePress>(this);
inputsListeners.mouseRelease.bind<GuiImpl, &GuiImpl::mouseRelease>(this);
inputsListeners.mouseMove.bind<GuiImpl, &GuiImpl::mouseMove>(this);
inputsListeners.mouseWheel.bind<GuiImpl, &GuiImpl::mouseWheel>(this);
inputsListeners.keyPress.bind<GuiImpl, &GuiImpl::keyPress>(this);
inputsListeners.keyRepeat.bind<GuiImpl, &GuiImpl::keyRepeat>(this);
inputsListeners.keyRelease.bind<GuiImpl, &GuiImpl::keyRelease>(this);
inputsListeners.keyChar.bind<GuiImpl, &GuiImpl::keyChar>(this);
inputsListeners.mousePress.bind([this](InputMouse in) { return this->mousePress(in); });
inputsListeners.mouseDoublePress.bind([this](InputMouse in) { return this->mouseDoublePress(in); });
inputsListeners.mouseRelease.bind([this](InputMouse in) { return this->mouseRelease(in); });
inputsListeners.mouseMove.bind([this](InputMouse in) { return this->mouseMove(in); });
inputsListeners.mouseWheel.bind([this](InputMouseWheel in) { return this->mouseWheel(in); });
inputsListeners.keyPress.bind([this](InputKey in) { return this->keyPress(in); });
inputsListeners.keyRepeat.bind([this](InputKey in) { return this->keyRepeat(in); });
inputsListeners.keyRelease.bind([this](InputKey in) { return this->keyRelease(in); });
inputsListeners.keyChar.bind([this](InputKey in) { return this->keyChar(in); });

skins.reserve(config.skinsCount);
for (uint32 i = 0; i < config.skinsCount; i++)
Expand All @@ -43,7 +43,7 @@ namespace cage
memory = newMemoryAllocatorStream({});

ttRemovedListener.attach(entityMgr->component<GuiTooltipMarkerComponent>()->group()->entityRemoved);
ttRemovedListener.bind<GuiImpl, &GuiImpl::tooltipRemoved>(this);
ttRemovedListener.bind([this](Entity *e) { return this->tooltipRemoved(e); });
}

GuiImpl::~GuiImpl()
Expand Down
2 changes: 1 addition & 1 deletion sources/libengine/inputs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ namespace cage
{
#define EVENT(CLASS, NAME, TYPE) \
if (generalizer) \
NAME.bind<InputsGeneralizer, &InputsGeneralizer::NAME>(generalizer); \
NAME.bind([generalizer](TYPE in) { return generalizer->NAME(in); }); \
else \
NAME.clear();

Expand Down
18 changes: 7 additions & 11 deletions sources/libsimple/fpsCamera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ namespace cage
public:
InputsDispatchers dispatchers;
InputsListeners listeners;
EventListener<bool(const GenericInput &)> windowListener;
EventListener<void()> updateListener;
const EventListener<bool(const GenericInput &)> windowListener = engineWindow()->events.listen([this](const GenericInput &in) { return this->dispatchers.dispatch(in); });
const EventListener<bool()> updateListener = controlThread().update.listen([this]() { return this->update(); });
VariableSmoothingBuffer<Vec2, 1> mouseSmoother;
VariableSmoothingBuffer<Vec3, 3> moveSmoother;
VariableSmoothingBuffer<Real, 2> wheelSmoother;
Expand All @@ -30,15 +30,11 @@ namespace cage
FpsCameraImpl(Entity *ent) : ent(ent)
{
listeners.attach(&dispatchers);
listeners.mousePress.bind<FpsCameraImpl, &FpsCameraImpl::mousePress>(this);
listeners.mouseMove.bind<FpsCameraImpl, &FpsCameraImpl::mouseMove>(this);
listeners.mouseWheel.bind<FpsCameraImpl, &FpsCameraImpl::mouseWheel>(this);
listeners.keyPress.bind<FpsCameraImpl, &FpsCameraImpl::keyPress>(this);
listeners.keyRelease.bind<FpsCameraImpl, &FpsCameraImpl::keyRelease>(this);
windowListener.bind<InputsDispatchers, &InputsDispatchers::dispatch>(&dispatchers);
windowListener.attach(engineWindow()->events);
updateListener.bind<FpsCameraImpl, &FpsCameraImpl::update>(this);
updateListener.attach(controlThread().update);
listeners.mousePress.bind([this](InputMouse in) { return this->mousePress(in); });
listeners.mouseMove.bind([this](InputMouse in) { return this->mouseMove(in); });
listeners.mouseWheel.bind([this](InputMouseWheel in) { return this->mouseWheel(in); });
listeners.keyPress.bind([this](InputKey in) { return this->keyPress(in); });
listeners.keyRelease.bind([this](InputKey in) { return this->keyRelease(in); });
}

Vec2 centerMouse()
Expand Down
14 changes: 4 additions & 10 deletions sources/libsimple/fullscreenSwitcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ namespace cage
class FullscreenSwitcherImpl : public FullscreenSwitcher
{
public:
InputListener<InputClassEnum::WindowMove, InputWindowValue> windowMoveListener;
InputListener<InputClassEnum::WindowResize, InputWindowValue> windowResizeListener;
InputListener<InputClassEnum::KeyRelease, InputKey, bool> keyListener;
Window *window = nullptr;
Window *const window = nullptr;
const EventListener<bool(const GenericInput &)> windowMoveListener = window->events.listen(inputListener<InputClassEnum::WindowMove, InputWindowValue>([this](InputWindowValue in) { return this->windowMove(in); }), -13665);
const EventListener<bool(const GenericInput &)> windowResizeListener = window->events.listen(inputListener<InputClassEnum::WindowResize, InputWindowValue>([this](InputWindowValue in) { return this->windowResize(in); }), -13666);
const EventListener<bool(const GenericInput &)> keyListener = window->events.listen(inputListener<InputClassEnum::KeyRelease, InputKey>([this](InputKey in) { return this->keyRelease(in); }), -13667);

ConfigSint32 confWindowLeft;
ConfigSint32 confWindowTop;
Expand All @@ -53,12 +53,6 @@ namespace cage
confFullscreenEnabled(confName(config, "window/fullscreen"), config.defaultFullscreen)
{
CAGE_ASSERT(window);
windowMoveListener.attach(window->events, -13665);
windowMoveListener.bind<FullscreenSwitcherImpl, &FullscreenSwitcherImpl::windowMove>(this);
windowResizeListener.attach(window->events, -13666);
windowResizeListener.bind<FullscreenSwitcherImpl, &FullscreenSwitcherImpl::windowResize>(this);
keyListener.attach(window->events, -13667);
keyListener.bind<FullscreenSwitcherImpl, &FullscreenSwitcherImpl::keyRelease>(this);
if (window->isHidden())
update(confFullscreenEnabled);
}
Expand Down
2 changes: 1 addition & 1 deletion sources/libsimple/gameloop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ namespace cage
c.provisionalGraphics = +provisionalGraphics;
gui = newGuiManager(c);
windowGuiEventsListener.attach(window->events);
windowGuiEventsListener.bind<GuiManager, &GuiManager::handleInput>(+gui);
windowGuiEventsListener.bind([gui = +gui](const GenericInput &in) { return gui->handleInput(in); });
}

{ // create sync objects
Expand Down
9 changes: 2 additions & 7 deletions sources/libsimple/guiInWorld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ namespace cage
guiMan->outputResolution(config.resolution);
guiMan->outputRetina(config.retinaScale);
}

graphicsDispatchListener.attach(graphicsDispatchThread().dispatch);
graphicsDispatchListener.bind<GuiInWorldImpl, &GuiInWorldImpl::dispatchEntry>(this);
updateListener.attach(controlThread().update);
updateListener.bind<GuiInWorldImpl, &GuiInWorldImpl::update>(this);
}

~GuiInWorldImpl()
Expand Down Expand Up @@ -199,8 +194,8 @@ namespace cage
uint32 modelName = 0;
bool firstFrame = true;

EventListener<void()> graphicsDispatchListener;
EventListener<void()> updateListener;
const EventListener<bool()> graphicsDispatchListener = graphicsDispatchThread().dispatch.listen([this]() { return this->dispatchEntry(); });
const EventListener<bool()> updateListener = controlThread().update.listen([this]() { return this->update(); });
};
}

Expand Down
15 changes: 4 additions & 11 deletions sources/libsimple/statisticsGui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ namespace cage
class StatisticsGuiImpl : public StatisticsGui
{
public:
InputListener<InputClassEnum::KeyPress, InputKey, bool> keyPressListener;
InputListener<InputClassEnum::KeyRepeat, InputKey, bool> keyRepeatListener;
EventListener<bool()> updateListener;
const EventListener<bool(const GenericInput &)> keyPressListener = engineWindow()->events.listen(inputListener<InputClassEnum::KeyPress, InputKey>([this](InputKey in) { return this->keyPress(in); }));
const EventListener<bool(const GenericInput &)> keyRepeatListener = engineWindow()->events.listen(inputListener<InputClassEnum::KeyRepeat, InputKey>([this](InputKey in) { return this->keyRepeat(in); }));
const EventListener<bool()> updateListener = controlThread().update.listen([this]() { return this->update(); });

uint32 panelIndex = 0;
uint32 layoutIndex = 0;
Expand All @@ -34,14 +34,7 @@ namespace cage
StatisticsGuiScopeEnum profilingModeOld = StatisticsGuiScopeEnum::Full;

StatisticsGuiImpl()
{
keyPressListener.bind<StatisticsGuiImpl, &StatisticsGuiImpl::keyPress>(this);
keyPressListener.attach(engineWindow()->events);
keyRepeatListener.bind<StatisticsGuiImpl, &StatisticsGuiImpl::keyRepeat>(this);
keyRepeatListener.attach(engineWindow()->events);
updateListener.bind<StatisticsGuiImpl, &StatisticsGuiImpl::update>(this);
updateListener.attach(controlThread().update);
}
{}

void nullData()
{
Expand Down
18 changes: 4 additions & 14 deletions sources/test-core/entities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,23 +170,13 @@ namespace
uint32 added = 0;
uint32 removed = 0;

EventListener<void(Entity *)> addListener;
EventListener<void(Entity *)> removeListener;
EventListener<bool(Entity *)> addListener;
EventListener<bool(Entity *)> removeListener;

Callbacks()
{
addListener.bind<Callbacks, &Callbacks::addEntity>(this);
removeListener.bind<Callbacks, &Callbacks::removeEntity>(this);
}

void addEntity(Entity *e)
{
added++;
}

void removeEntity(Entity *e)
{
removed++;
addListener.bind([&](Entity *) { added++; });
removeListener.bind([&](Entity *) { removed++; });
}
} manCbs, posCbs, oriCbs;

Expand Down
Loading

0 comments on commit e9efd50

Please sign in to comment.