Skip to content

Commit

Permalink
keybinds explicit flags
Browse files Browse the repository at this point in the history
malytomas committed Jan 28, 2025
1 parent 51b0d01 commit d041532
Showing 3 changed files with 79 additions and 28 deletions.
30 changes: 19 additions & 11 deletions sources/include/cage-engine/keybinds.h
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ namespace cage
String name() const;
String value(uint32 index = 0) const;
bool process(const GenericInput &input) const;
void setActive(bool a);
bool active() const;

uint32 count() const;
@@ -43,14 +44,18 @@ namespace cage
enum class KeybindModesFlags : uint32
{
None = 0,
Press = 1 << 0,
Repeat = 1 << 1,
Double = 1 << 2,
Release = 1 << 3,
Scroll = 1 << 4,
Tick = 1 << 5,
KeyPress = 1 << 0,
KeyRepeat = 1 << 1,
KeyRelease = 1 << 2,
MousePress = 1 << 3,
MouseDouble = 1 << 4,
MouseRelease = 1 << 5,
WheelScroll = 1 << 6,
GameTick = 1 << 7,
EngineTick = 1 << 8,
};
GCHL_ENUM_BITS(KeybindModesFlags);
CAGE_ENGINE_API KeybindModesFlags keybindMode(const GenericInput &in);

struct CAGE_ENGINE_API KeybindCreateConfig
{
@@ -59,22 +64,25 @@ namespace cage
sint32 displayOrder = 0;
ModifiersFlags requiredFlags = ModifiersFlags::None;
ModifiersFlags forbiddenFlags = ModifiersFlags::Super;
KeybindDevicesFlags devices = KeybindDevicesFlags::Keyboard | KeybindDevicesFlags::Mouse;
KeybindModesFlags modes = KeybindModesFlags::Press;
bool exactFlags = true;
KeybindDevicesFlags devices = KeybindDevicesFlags::Keyboard | KeybindDevicesFlags::Mouse; // which devices are allowed to bind
KeybindModesFlags modes = KeybindModesFlags::KeyPress | KeybindModesFlags::MousePress; // which modes trigger the event (contrary: activation is always with press and release)
};

CAGE_ENGINE_API Holder<Keybind> newKeybind(const KeybindCreateConfig &config, const GenericInput &defaults = {}, Delegate<bool(const GenericInput &)> event = {});
CAGE_ENGINE_API Holder<Keybind> newKeybind(const KeybindCreateConfig &config, PointerRange<const GenericInput> defaults, Delegate<bool(const GenericInput &)> event = {});
CAGE_ENGINE_API Keybind *findKeybind(const String &id);

CAGE_ENGINE_API void keybindsRegisterListeners(EventDispatcher<bool(const GenericInput &)> &dispatcher);

namespace input
{
struct CAGE_ENGINE_API Tick
struct CAGE_ENGINE_API GameTick
{};
struct CAGE_ENGINE_API EngineTick
{};
}
CAGE_ENGINE_API void keybindsDispatchTick();
CAGE_ENGINE_API void keybindsDispatchGameTick();
CAGE_ENGINE_API void keybindsDispatchEngineTick();

CAGE_ENGINE_API void keybindsGuiWidget(guiBuilder::GuiBuilder *g, Keybind *keybind);
CAGE_ENGINE_API void keybindsGuiTable(guiBuilder::GuiBuilder *g, const String &filterPrefix = {});
73 changes: 56 additions & 17 deletions sources/libengine/keybinds.cpp
Original file line number Diff line number Diff line change
@@ -44,20 +44,20 @@ namespace cage

CAGE_FORCE_INLINE KeybindModesFlags matchImpl(input::privat::BaseKey k, KeybindModesFlags activation) const
{
if (k.key == key && (checkFlags(k.mods) || activation == KeybindModesFlags::Release))
if ((k.key == key || k.key == m) && (checkFlags(k.mods) || activation == KeybindModesFlags::KeyRelease))
return activation;
return KeybindModesFlags::None;
}

template<class T>
CAGE_FORCE_INLINE KeybindModesFlags match(const GenericInput &input, KeybindModesFlags activation) const
CAGE_FORCE_INLINE KeybindModesFlags match(const GenericInput &input) const
{
if (input.has<T>())
return matchImpl(input.get<T>(), activation);
return matchImpl(input.get<T>(), keybindMode(input));
return KeybindModesFlags::None;
}

CAGE_FORCE_INLINE KeybindModesFlags match(const GenericInput &input) const { return match<input::KeyPress>(input, KeybindModesFlags::Press) | match<input::KeyRepeat>(input, KeybindModesFlags::Repeat) | match<input::KeyRelease>(input, KeybindModesFlags::Release); }
CAGE_FORCE_INLINE KeybindModesFlags match(const GenericInput &input) const { return match<input::KeyPress>(input) | match<input::KeyRepeat>(input) | match<input::KeyRelease>(input); }

CAGE_FORCE_INLINE String value() const { return finishName(Stringizer() + getModifiersNames(requiredFlags) + " " + getKeyName(key)); }
};
@@ -68,20 +68,20 @@ namespace cage

CAGE_FORCE_INLINE KeybindModesFlags matchImpl(input::privat::BaseMouse k, KeybindModesFlags activation) const
{
if (any(k.buttons & button) && (checkFlags(k.mods) || activation == KeybindModesFlags::Release))
if ((any(k.buttons & button) || k.buttons == (MouseButtonsFlags)m) && (checkFlags(k.mods) || activation == KeybindModesFlags::MouseRelease))
return activation;
return KeybindModesFlags::None;
}

template<class T>
CAGE_FORCE_INLINE KeybindModesFlags match(const GenericInput &input, KeybindModesFlags activation) const
CAGE_FORCE_INLINE KeybindModesFlags match(const GenericInput &input) const
{
if (input.has<T>())
return matchImpl(input.get<T>(), activation);
return matchImpl(input.get<T>(), keybindMode(input));
return KeybindModesFlags::None;
}

CAGE_FORCE_INLINE KeybindModesFlags match(const GenericInput &input) const { return match<input::MousePress>(input, KeybindModesFlags::Press) | match<input::MouseDoublePress>(input, KeybindModesFlags::Double) | match<input::MouseRelease>(input, KeybindModesFlags::Release); }
CAGE_FORCE_INLINE KeybindModesFlags match(const GenericInput &input) const { return match<input::MousePress>(input) | match<input::MouseDoublePress>(input) | match<input::MouseRelease>(input); }

CAGE_FORCE_INLINE String value() const { return finishName(Stringizer() + getModifiersNames(requiredFlags) + " " + getButtonsNames(button)); }
};
@@ -93,7 +93,7 @@ namespace cage
if (input.has<input::MouseWheel>())
{
if (checkFlags(input.get<input::MouseWheel>().mods))
return KeybindModesFlags::Scroll;
return KeybindModesFlags::WheelScroll;
}
return KeybindModesFlags::None;
}
@@ -126,6 +126,8 @@ namespace cage
MatcherBase r;
r.requiredFlags = config.requiredFlags | mods;
r.forbiddenFlags = config.forbiddenFlags;
if (config.exactFlags)
r.forbiddenFlags |= ~r.requiredFlags;
CAGE_ASSERT(none(r.requiredFlags & r.forbiddenFlags));
return r;
}
@@ -222,25 +224,26 @@ namespace cage
active = false;
return false;
}
if (input.has<input::Tick>())
if (active)
{
if (active && any(config.modes & KeybindModesFlags::Tick))
if (input.has<input::GameTick>() && any(config.modes & KeybindModesFlags::GameTick))
return event(input);
if (input.has<input::EngineTick>() && any(config.modes & KeybindModesFlags::EngineTick))
return event(input);
return false;
}
for (const auto &it : matchers)
{
const KeybindModesFlags r = matches(input, it);
if (any(r & (KeybindModesFlags::Press | KeybindModesFlags::Repeat)))
if (any(r & (KeybindModesFlags::KeyPress | KeybindModesFlags::MousePress)))
active = true;
if (any(r & KeybindModesFlags::Release))
if (any(r & (KeybindModesFlags::KeyRelease | KeybindModesFlags::MouseRelease)))
active = false;
if (any(r & config.modes))
{
if (event)
{
const bool p = event(input);
if (none(r & KeybindModesFlags::Release)) // release always propagates
if (none(r & (KeybindModesFlags::KeyRelease | KeybindModesFlags::MouseRelease))) // release always propagates
return p;
}
return false;
@@ -401,6 +404,12 @@ namespace cage
return impl->process(input);
}

void Keybind::setActive(bool a)
{
KeybindImpl *impl = (KeybindImpl *)this;
impl->active = a;
}

bool Keybind::active() const
{
const KeybindImpl *impl = (const KeybindImpl *)this;
@@ -455,6 +464,29 @@ namespace cage
CAGE_ASSERT(impl->matchers.size() == impl->defaults.size());
}

KeybindModesFlags keybindMode(const GenericInput &in)
{
if (in.has<input::KeyPress>())
return KeybindModesFlags::KeyPress;
if (in.has<input::KeyRepeat>())
return KeybindModesFlags::KeyRepeat;
if (in.has<input::KeyRelease>())
return KeybindModesFlags::KeyRelease;
if (in.has<input::MousePress>())
return KeybindModesFlags::MousePress;
if (in.has<input::MouseDoublePress>())
return KeybindModesFlags::MouseDouble;
if (in.has<input::MouseRelease>())
return KeybindModesFlags::MouseRelease;
if (in.has<input::MouseWheel>())
return KeybindModesFlags::WheelScroll;
if (in.has<input::GameTick>())
return KeybindModesFlags::GameTick;
if (in.has<input::EngineTick>())
return KeybindModesFlags::EngineTick;
return KeybindModesFlags::None;
}

Holder<Keybind> newKeybind(const KeybindCreateConfig &config, const GenericInput &defaults, Delegate<bool(const GenericInput &)> event)
{
return newKeybind(config, PointerRange(&defaults, &defaults + 1), event);
@@ -486,9 +518,16 @@ namespace cage
}
}

void keybindsDispatchTick()
void keybindsDispatchGameTick()
{
GenericInput g = input::GameTick();
for (KeybindImpl *it : global())
it->process(g);
}

void keybindsDispatchEngineTick()
{
GenericInput g = input::Tick();
GenericInput g = input::EngineTick();
for (KeybindImpl *it : global())
it->process(g);
}
4 changes: 4 additions & 0 deletions sources/libsimple/gameloop.cpp
Original file line number Diff line number Diff line change
@@ -353,6 +353,10 @@ namespace cage
virtualReality->processEvents();
virtualRealitySceneUpdate(+entities);
}
{
ProfilingScope profiling("keybinds engine tick");
keybindsDispatchEngineTick();
}
{
ProfilingScope profiling("control callback");
controlThread().update.dispatch();

0 comments on commit d041532

Please sign in to comment.