diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2023-11-15 13:03:48 -0500 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2023-11-15 13:03:48 -0500 |
| commit | 1513216876a0409f45d88cbad14ae8b48fca37e2 (patch) | |
| tree | 32b48d966faf36138dd985e4df739a83cddc5f48 /src/player-input.cpp | |
| parent | 6a30d21c5175c27aeccc8a6ae956c274f3ef9621 (diff) | |
major input refactor and add debug camera
Diffstat (limited to 'src/player-input.cpp')
| -rw-r--r-- | src/player-input.cpp | 762 |
1 files changed, 633 insertions, 129 deletions
diff --git a/src/player-input.cpp b/src/player-input.cpp index 4b2b6be..9f50307 100644 --- a/src/player-input.cpp +++ b/src/player-input.cpp @@ -1,9 +1,38 @@ #include "player-input.hpp" -#include "window.hpp" + +#include "dynamic-array.hpp" #include "game-settings.hpp" +#include "window.hpp" + +#include <GLFW/glfw3.h> #include <chrono> +struct CursorEnterEvent { + bool entered; +}; +struct CursorPosEvent { + double x; + double y; +}; +struct KeyEvent { + int32_t button; + int32_t mods; + int8_t action; +}; +struct MouseButtonEvent { + int32_t button; + int32_t mods; + int8_t action; +}; +struct ScrollEvent { + double x; + double y; +}; +struct WindowFocusEvent { + bool focused; +}; + GLFWcursorenterfun prevCursorEnterCallback; GLFWcursorposfun prevCursorPosCallback; GLFWkeyfun prevKeyCallback; @@ -11,166 +40,500 @@ GLFWmousebuttonfun prevMouseButtonCallback; GLFWscrollfun prevScrollCallback; GLFWwindowfocusfun prevWindowFocusCallback; -TypeSafeInt_B(InputEventHash); -TypeSafeInt_B(InputEventType); -TypeSafeInt_B(InputCursorEntered); -TypeSafeInt_B(InputKeyboardKeyAction); -TypeSafeInt_B(InputMouseButtonAction); -TypeSafeInt_B(InputWindowFocused); - -DynArray<PkeInputEvent> UnhandledPkeInputEvents{0, nullptr}; - -bool PkeInput_Query(const PkeInputEvent &mask) { - auto count = UnhandledPkeInputEvents.Count(); - for (long i = 0; i < count; ++i) { - const auto &ev = UnhandledPkeInputEvents[i]; - // TODO this could probably be reduced to a single & op - // - if we exclude x, y, button, scanCode, and mods - // there are less than 64 options total - if (!(mask.type == ev.type)) continue; - if (!(mask.tickCount == ev.tickCount)) continue; - if (!(mask.cursorEntered == ev.cursorEntered)) continue; - if (!(mask.keyboardKeyAction == ev.keyboardKeyAction)) continue; - if (!(mask.mouseButtonAction == ev.mouseButtonAction)) continue; - if (!(mask.windowFocused == ev.windowFocused)) continue; - if (!(mask.x == ev.x)) continue; - if (!(mask.y == ev.y)) continue; - if (!(mask.button == ev.button)) continue; - if (!(mask.scanCode == ev.scanCode)) continue; - if (!(mask.mods == ev.mods)) continue; - UnhandledPkeInputEvents.Remove(i); - return true; - } - return false; +TypeSafeInt_B(InputActionSetHandle); + +DynArray<CursorPosEvent> unhandledCursorPosEvents{512, nullptr}; +DynArray<CursorEnterEvent> unhandledCursorEnterEvents{8, nullptr}; +DynArray<KeyEvent> unhandledKeyEvents{8, nullptr}; +DynArray<MouseButtonEvent> unhandledMouseButtonEvents{8, nullptr}; +DynArray<ScrollEvent> unhandledScrollEvents{8, nullptr}; +DynArray<WindowFocusEvent> unhandledWindowFocusEvents{8, nullptr}; + +DynArray<PkeCursorEnterEvent> registeredCursorEnterEvents{8, nullptr}; +bool lastCursorEntered = false; +DynArray<PkeCursorPosEvent> registeredCursorPosEvents{8, nullptr}; +struct { + double x = 0; + double y = 0; +} lastMousePos; +DynArray<PkeKeyEvent> registeredKeyEvents{8, nullptr}; +DynArray<PkeMouseButtonEvent> registeredMouseButtonEvents{8, nullptr}; +DynArray<PkeScrollEvent> registeredScrollEvents{8, nullptr}; +DynArray<PkeWindowFocusEvent> registeredWindowFocusEvents{8, nullptr}; +bool lastWindowFocus = false; + +// DynArray<PkeInputEvent> UnhandledPkeInputEvents{1024, nullptr}; + +DynArray<PkeInputSet> registeredInputSets{0, nullptr}; +DynArray<InputActionSetHandle> activeInputSetStack{0, nullptr}; + +PkeInputAction *FindActionByName(const char *actionName) { + int64_t count = 0; + count = activeInputSetStack.Count(); + for (int64_t i = count - 1; i >= 0; --i) { + InputActionSetHandle handle = activeInputSetStack[i]; + InputActionSetHandle_T index = static_cast<InputActionSetHandle_T>(handle); + PkeInputSet &set = registeredInputSets[index]; + for (int64_t k = 0; k < set.actionCount; ++k) { + PkeInputAction &inputAction = set.actions[k]; + if (strcmp(actionName, inputAction.name) != 0) { + continue; + }; + return &inputAction; + } + } + return nullptr; +} +template<InputEventHash T, typename S> InputEventHash FindActivePkeInputAction_ByAction(const PkeInputAction *inputAction, S *&activeEvent) { + activeEvent = nullptr; + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + activeEvent = ®isteredCursorEnterEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + activeEvent = ®isteredCursorPosEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + activeEvent = ®isteredKeyEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_KEY; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + activeEvent = ®isteredMouseButtonEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + activeEvent = ®isteredScrollEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_SCROLL; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + activeEvent = ®isteredWindowFocusEvents[inputAction->eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS; + } + return InputEventHash{0}; } +template<InputEventHash T, typename S> InputEventHash FindActivePkeInputAction_ByName(const char *actionName, S *&activeEvent) { + activeEvent = nullptr; + bool any = false; + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + any = any || bool(registeredCursorEnterEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + any = any || bool(registeredCursorPosEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + any = any || bool(registeredKeyEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + any = any || bool(registeredMouseButtonEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + any = any || bool(registeredScrollEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + any = any || bool(registeredWindowFocusEvents.Count()); + } + if (any == false) return InputEventHash{0}; + int64_t count = 0; + count = activeInputSetStack.Count(); + for (int64_t i = count - 1; i >= 0; --i) { + InputActionSetHandle handle = activeInputSetStack[i]; + InputActionSetHandle_T index = static_cast<InputActionSetHandle_T>(handle); + PkeInputSet &set = registeredInputSets[index]; + for (int64_t k = 0; k < set.actionCount; ++k) { + PkeInputAction &inputAction = set.actions[k]; + if (strcmp(actionName, inputAction.name) != 0) { + continue; + }; + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + activeEvent = ®isteredCursorEnterEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + activeEvent = ®isteredCursorPosEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + activeEvent = ®isteredKeyEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_KEY; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + activeEvent = ®isteredMouseButtonEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + activeEvent = ®isteredScrollEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_SCROLL; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + activeEvent = ®isteredWindowFocusEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS; + } + } + } + return InputEventHash{0}; +} +template<InputEventHash T, typename S> InputEventHash FindActivePkeInputAction_ByType(const PkeInputEventMask &mask, S *&activeEvent) { + activeEvent = nullptr; + bool any = false; + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + any = any || bool(registeredCursorEnterEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + any = any || bool(registeredCursorPosEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + any = any || bool(registeredKeyEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + any = any || bool(registeredMouseButtonEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + any = any || bool(registeredScrollEvents.Count()); + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + any = any || bool(registeredWindowFocusEvents.Count()); + } + if (any == false) return InputEventHash{0}; + int64_t count = 0; + count = activeInputSetStack.Count(); + for (int64_t i = count - 1; i >= 0; --i) { + InputActionSetHandle handle = activeInputSetStack[i]; + InputActionSetHandle_T index = static_cast<InputActionSetHandle_T>(handle); + PkeInputSet &set = registeredInputSets[index]; + for (int64_t k = 0; k < set.actionCount; ++k) { + PkeInputAction &inputAction = set.actions[k]; + for (int64_t l = 0; l < PKE_INPUT_ACTION_MASK_COUNT; ++l) { + PkeInputEventMask &evMask = inputAction.masks[l]; + if ((evMask.computedHash & mask.computedHash) == InputEventHash{0}) { + continue; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + if (evMask.button != mask.button) { + continue; + } + if (evMask.mods != mask.mods) { + continue; + } + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + if (evMask.button != mask.button) { + continue; + } + if (evMask.mods != mask.mods) { + continue; + } + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + activeEvent = ®isteredCursorEnterEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + activeEvent = ®isteredCursorPosEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + activeEvent = ®isteredKeyEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_KEY; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + activeEvent = ®isteredMouseButtonEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + activeEvent = ®isteredScrollEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_SCROLL; + } + if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + activeEvent = ®isteredWindowFocusEvents[inputAction.eventIndex]; + return PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS; + } + } + } + } + return InputEventHash{0}; +} +constexpr auto FindActivePkeInputAction_CursorEnter_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER, PkeCursorEnterEvent>; +constexpr auto FindActivePkeInputAction_CursorPos_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS, PkeCursorPosEvent>; +constexpr auto FindActivePkeInputAction_Key_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_KEY, PkeKeyEvent>; +constexpr auto FindActivePkeInputAction_MouseButton_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON, PkeMouseButtonEvent>; +constexpr auto FindActivePkeInputAction_Scroll_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_SCROLL, PkeScrollEvent>; +constexpr auto FindActivePkeInputAction_WindowFocus_ByType = FindActivePkeInputAction_ByType<PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS, PkeWindowFocusEvent>; + +constexpr auto FindActivePkeInputAction_CursorEnter_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER, PkeCursorEnterEvent>; +constexpr auto FindActivePkeInputAction_CursorPos_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS, PkeCursorPosEvent>; +constexpr auto FindActivePkeInputAction_Key_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_KEY, PkeKeyEvent>; +constexpr auto FindActivePkeInputAction_MouseButton_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON, PkeMouseButtonEvent>; +constexpr auto FindActivePkeInputAction_Scroll_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_SCROLL, PkeScrollEvent>; +constexpr auto FindActivePkeInputAction_WindowFocus_ByName = FindActivePkeInputAction_ByName<PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS, PkeWindowFocusEvent>; + void PkeInput_Tick(double delta) { - // If we haven't handled an event within two seconds, we probably don't need it - static GameTimeDuration eventExpiration(std::chrono::seconds(2)); - int64_t removeCount = 0; - int64_t unhandledCount = UnhandledPkeInputEvents.Count(); - auto expiredTime = pkeSettings.steadyClock.now() - eventExpiration; - for (int64_t i = 0; i < unhandledCount; ++i) { - auto &ev = UnhandledPkeInputEvents[i]; - ev.tickCount += 1; - if (ev.timePoint < expiredTime) { - removeCount += 1; + // reset everything + // @performance could happen concurrently + { + int64_t count = 0; + count = registeredCursorPosEvents.Count(); + for (int64_t i = 0; i < count; ++i) { + auto &ev = registeredCursorPosEvents[i]; + ev.xMotion = 0; + ev.yMotion = 0; + } + count = registeredKeyEvents.Count(); + for (int64_t i = 0; i < count; ++i) { + auto &ev = registeredKeyEvents[i]; + // TODO the idea here was right, but wrong place and too wide a swath + // if (i < count - 1) ev.isPressed = false; + ev.pressCount = 0; + } + count = registeredMouseButtonEvents.Count(); + for (int64_t i = 0; i < count; ++i) { + auto &ev = registeredMouseButtonEvents[i]; + // TODO the idea here was right, but wrong place and too wide a swath + // if (i < count - 1) ev.isPressed = false; + ev.pressCount = 0; + } + count = registeredScrollEvents.Count(); + for (int64_t i = 0; i < count; ++i) { + auto &ev = registeredScrollEvents[i]; + ev.xMotion = 0; + ev.yMotion = 0; + } + } + + // handle unhandled events + // @performance cache action->event results + { + do { // while (0) + PkeCursorEnterEvent *primaryEvent = nullptr; + FindActivePkeInputAction_CursorEnter_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_CURSOR_ENTER_EVENTS + }, primaryEvent); + if (primaryEvent == nullptr) { + break; + } + const auto &ev = unhandledCursorEnterEvents[unhandledCursorEnterEvents.Count() - 1]; + primaryEvent->isEntered = ev.entered; + lastCursorEntered = ev.entered; + } while (0); + do { // while (0) + PkeCursorPosEvent *primaryEvent = nullptr; + FindActivePkeInputAction_CursorPos_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_CURSOR_POS_EVENTS + }, primaryEvent); + if (primaryEvent == nullptr) { + break; + } + int64_t count = unhandledCursorPosEvents.Count(); + for (long i = 0; i < count; ++i) { + const auto &ev = unhandledCursorPosEvents[i]; + primaryEvent->xMotion += ev.x - lastMousePos.x; + primaryEvent->yMotion += ev.y - lastMousePos.y; + lastMousePos.x = ev.x; + lastMousePos.y = ev.y; + } + } while (0); + do { // while (0) + int64_t count = unhandledKeyEvents.Count(); + for (long i = 0; i < count; ++i) { + const auto &ev = unhandledKeyEvents[i]; + PkeKeyEvent *primaryEvent = nullptr; + FindActivePkeInputAction_Key_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, + .button = ev.button, + .mods = ev.mods, + }, primaryEvent); + if (primaryEvent == nullptr) { + continue; + } + if (ev.action == GLFW_PRESS) { + primaryEvent->isPressed = true; + } else if (ev.action == GLFW_RELEASE) { + if (primaryEvent->isPressed == true) { + primaryEvent->pressCount += 1; + } + primaryEvent->isPressed = false; + } else { + // repeat + primaryEvent->isPressed = true; + primaryEvent->pressCount += 1; + } + } + } while (0); + do { // while (0) + int64_t count = unhandledMouseButtonEvents.Count(); + for (long i = 0; i < count; ++i) { + const auto &ev = unhandledMouseButtonEvents[i]; + PkeMouseButtonEvent *primaryEvent = nullptr; + FindActivePkeInputAction_MouseButton_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS, + .button = ev.button, + .mods = ev.mods, + }, primaryEvent); + if (primaryEvent == nullptr) { + continue; + } + if (ev.action == GLFW_PRESS) { + primaryEvent->isPressed = true; + } else if (ev.action == GLFW_RELEASE) { + if (primaryEvent->isPressed == true) { + primaryEvent->pressCount += 1; + } + primaryEvent->isPressed = false; + } + } + } while (0); + do { // while (0) + PkeScrollEvent *primaryEvent = nullptr; + FindActivePkeInputAction_Scroll_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_SCROLL_EVENTS + }, primaryEvent); + if (primaryEvent == nullptr) { + break; + } + int64_t count = unhandledScrollEvents.Count(); + for (long i = 0; i < count; ++i) { + const auto &ev = unhandledScrollEvents[i]; + primaryEvent->xMotion += ev.x; + primaryEvent->yMotion += ev.y; + } + } while (0); + do { // while (0) + PkeWindowFocusEvent *primaryEvent = nullptr; + FindActivePkeInputAction_WindowFocus_ByType(PkeInputEventMask { + .computedHash = PKE_INPUT_HASH_ALL_WINDOW_FOCUS_EVENTS + }, primaryEvent); + if (primaryEvent == nullptr) { + break; + } + const auto &ev = unhandledWindowFocusEvents[unhandledWindowFocusEvents.Count() - 1]; + primaryEvent->isFocused = ev.focused; + lastWindowFocus = ev.focused; + } while (0); + } + unhandledCursorEnterEvents.Resize(0); + unhandledCursorPosEvents.Resize(0); + unhandledKeyEvents.Resize(0); + unhandledMouseButtonEvents.Resize(0); + unhandledScrollEvents.Resize(0); + unhandledWindowFocusEvents.Resize(0); + + // set boolean events with latest data + // - we do this after processing events to make sure that if a set is + // unregistered, the appropriate values are propagated + // - theoretically this scenario could be handled on unregister, + // but I'm not sure which would be more performant + { + int64_t count = 0; + count = registeredCursorEnterEvents.Count(); + for (int64_t i = 0; i < count - 1; ++i) { + auto &ev = registeredCursorEnterEvents[i]; + ev.isEntered = lastCursorEntered; + } + count = registeredWindowFocusEvents.Count(); + for (int64_t i = 0; i < count - 1; ++i) { + auto &ev = registeredWindowFocusEvents[i]; + ev.isFocused = lastWindowFocus; } } - if (removeCount > 0) { - UnhandledPkeInputEvents.Remove(0, removeCount); +} + +PkeInputEventHolder PkeInput_Query(const char *actionName) { + PkeInputEventBase *ev = nullptr; + auto *action = FindActionByName(actionName); + InputEventHash type = InputEventHash{0}; + if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER)) { + PkeCursorEnterEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER, PkeCursorEnterEvent>(action, event); + ev = event; + } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS)) { + PkeCursorPosEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS, PkeCursorPosEvent>(action, event); + ev = event; + } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY)) { + PkeKeyEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_KEY, PkeKeyEvent>(action, event); + ev = event; + } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON)) { + PkeMouseButtonEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON, PkeMouseButtonEvent>(action, event); + ev = event; + } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL)) { + PkeScrollEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_SCROLL, PkeScrollEvent>(action, event); + ev = event; + } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS)) { + PkeWindowFocusEvent *event; + type = FindActivePkeInputAction_ByAction<PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS, PkeWindowFocusEvent>(action, event); + ev = event; } + return PkeInputEventHolder { + .type = type, + .ptr = ev, + }; } void CursorEnterCallback(GLFWwindow *window, int entered) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_CURSOR_ENTER; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = InputCursorEntered{static_cast<InputCursorEntered_T>(entered)}; - ev.keyboardKeyAction = PKE_INPUT_KEYBOARD_KEY_ACTION_UNSET; - ev.mouseButtonAction = PKE_INPUT_MOUSE_BUTTON_ACTION_UNSET; - ev.windowFocused = PKE_INPUT_WINDOW_FOCUSED_UNSET; - ev.x = 0; - ev.y = 0; - ev.button = 0; - ev.scanCode = 0; - ev.mods = 0; - ev.tickCount = 0; + if (registeredCursorEnterEvents.Count()) { + auto &ev = unhandledCursorEnterEvents.Push(); + ev.entered = bool(entered); + } if (prevCursorEnterCallback) { prevCursorEnterCallback(window, entered); } } void CursorPosCallback(GLFWwindow *window, double xPos, double yPos) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_CURSOR_POS; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = PKE_INPUT_CURSOR_ENTERED_UNSET; - ev.keyboardKeyAction = PKE_INPUT_KEYBOARD_KEY_ACTION_UNSET; - ev.mouseButtonAction = PKE_INPUT_MOUSE_BUTTON_ACTION_UNSET; - ev.windowFocused = PKE_INPUT_WINDOW_FOCUSED_UNSET; - ev.x = xPos; - ev.y = yPos; - ev.button = 0; - ev.scanCode = 0; - ev.mods = 0; - ev.tickCount = 0; + if (registeredCursorPosEvents.Count()) { + auto &ev = unhandledCursorPosEvents.Push(); + ev.x = xPos; + ev.y = yPos; + } if (prevCursorPosCallback) { prevCursorPosCallback(window, xPos, yPos); } } void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_KEY; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = PKE_INPUT_CURSOR_ENTERED_UNSET; - ev.keyboardKeyAction = InputKeyboardKeyAction{static_cast<InputKeyboardKeyAction_T>(action)}; - ev.mouseButtonAction = PKE_INPUT_MOUSE_BUTTON_ACTION_UNSET; - ev.windowFocused = PKE_INPUT_WINDOW_FOCUSED_UNSET; - ev.x = 0; - ev.y = 0; - ev.button = key; - ev.scanCode = scancode; - ev.mods = mods; - ev.tickCount = 0; + if (registeredKeyEvents.Count()) { + auto &ev = unhandledKeyEvents.Push(); + ev.button = key; + ev.mods = mods & 0x0F; + ev.action = action; + } if (prevKeyCallback) { prevKeyCallback(window, key, scancode, action, mods); } } void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_MOUSE_BUTTON; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = PKE_INPUT_CURSOR_ENTERED_UNSET; - ev.keyboardKeyAction = PKE_INPUT_KEYBOARD_KEY_ACTION_UNSET; - ev.mouseButtonAction = InputMouseButtonAction{static_cast<InputMouseButtonAction_T>(action)}; - ev.windowFocused = PKE_INPUT_WINDOW_FOCUSED_UNSET; - ev.x = 0; - ev.y = 0; - ev.button = button; - ev.scanCode = 0; - ev.mods = mods; - ev.tickCount = 0; + if (registeredMouseButtonEvents.Count()) { + auto &ev = unhandledMouseButtonEvents.Push(); + ev.button = button; + ev.mods = mods; + ev.action = action; + } if (prevMouseButtonCallback) { prevMouseButtonCallback(window, button, action, mods); } } void ScrollCallback(GLFWwindow *window, double xOffset, double yOffset) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_SCROLL; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = PKE_INPUT_CURSOR_ENTERED_UNSET; - ev.keyboardKeyAction = PKE_INPUT_KEYBOARD_KEY_ACTION_UNSET; - ev.mouseButtonAction = PKE_INPUT_MOUSE_BUTTON_ACTION_UNSET; - ev.windowFocused = PKE_INPUT_WINDOW_FOCUSED_UNSET; - ev.x = xOffset; - ev.y = yOffset; - ev.button = 0; - ev.scanCode = 0; - ev.mods = 0; - ev.tickCount = 0; + if (registeredScrollEvents.Count()) { + auto &ev = unhandledScrollEvents.Push(); + ev.x = xOffset; + ev.y = yOffset; + } if (prevScrollCallback) { prevScrollCallback(window, xOffset, yOffset); } } void WindowFocusCallback(GLFWwindow *window, int focused) { - auto &ev = UnhandledPkeInputEvents.Push(); - ev.type = PKE_INPUT_EVENT_TYPE_WINDOW_FOCUS; - ev.timePoint = pkeSettings.steadyClock.now(); - ev.cursorEntered = PKE_INPUT_CURSOR_ENTERED_UNSET; - ev.keyboardKeyAction = PKE_INPUT_KEYBOARD_KEY_ACTION_UNSET; - ev.mouseButtonAction = PKE_INPUT_MOUSE_BUTTON_ACTION_UNSET; - ev.windowFocused = InputWindowFocused{static_cast<InputWindowFocused_T>(focused)}; - ev.x = 0; - ev.y = 0; - ev.button = 0; - ev.scanCode = 0; - ev.mods = 0; - ev.tickCount = 0; + if (registeredWindowFocusEvents.Count()) { + auto &ev = unhandledWindowFocusEvents.Push(); + ev.focused = focused; + } if (prevWindowFocusCallback) { prevWindowFocusCallback(window, focused); } @@ -185,13 +548,154 @@ void PkeInput_Init() { prevWindowFocusCallback = glfwSetWindowFocusCallback(window, WindowFocusCallback); } +InputActionSetHandle PkeInput_RegisterSet(const PkeInputSet &set) { + InputActionSetHandle returnValue{static_cast<InputActionSetHandle_T>(registeredInputSets.Count())}; + registeredInputSets.Push(set); + return returnValue; +} + +void PkeInput_ActivateSet(InputActionSetHandle handle) { + InputActionSetHandle_T index{static_cast<InputActionSetHandle_T>(handle)}; + activeInputSetStack.Push(handle); + auto &set = registeredInputSets[index]; + for (int64_t i = 0; i < set.actionCount; ++i) { + PkeInputAction &action = set.actions[i]; + // TODO doesn't support multiple masks - how do we align multiple event types? + // EX: strafe with keyboard + joystick + // creating multiple events doesn't make sense + // - have to keep in sync - isPressed ? + // - caller has to handle multiple scenarios + // Maybe we need a unifying event, but that defeats the purpose of this refactor and wastes memory + // Not to mention that the caller still has to know how to handle the unified response + // Some combinations of event types cannot be combined, so the caller has to know to ignore certain values + // Current design also has flaws + // The caller explicitly casts to the correct event type because we know the event type. + // If bindings were loaded dynamically, we would have to handle every scenario explicitly for every action + // Godot has some similarities - is_action_pressed(name) could hide the type, and Godot expects the + // programmer to know the input type in order to handle + // If we went for a more query-based approach - PkeInput_GetPressCount(actionTitle) for example + // there might be fewer oddities + // Maybe you just have to restrict certain actions to certain input types + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + action.eventIndex = registeredCursorEnterEvents.Count(); + registeredCursorEnterEvents.Push(PkeCursorEnterEvent { + .sourceSet = handle, + .isEntered = lastCursorEntered, + }); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + action.eventIndex = registeredCursorPosEvents.Count(); + registeredCursorPosEvents.Push(PkeCursorPosEvent { + .sourceSet = handle, + .xMotion = 0, + .yMotion = 0, + }); + glfwGetCursorPos(window, &lastMousePos.x, &lastMousePos.y); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + action.eventIndex = registeredKeyEvents.Count(); + registeredKeyEvents.Push(PkeKeyEvent { + .sourceSet = handle, + .button = action.primaryHash.button, + .mods = action.primaryHash.mods, + }); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + action.eventIndex = registeredMouseButtonEvents.Count(); + registeredMouseButtonEvents.Push(PkeMouseButtonEvent { + .sourceSet = handle, + .button = action.primaryHash.button, + .mods = action.primaryHash.mods, + }); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + action.eventIndex = registeredScrollEvents.Count(); + registeredScrollEvents.Push(PkeScrollEvent { + .sourceSet = handle, + .xMotion = 0, + .yMotion = 0, + }); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + action.eventIndex = registeredWindowFocusEvents.Count(); + registeredWindowFocusEvents.Push(PkeWindowFocusEvent { + .sourceSet = handle, + .isFocused = lastWindowFocus, + }); + } + } +} + +void PkeInput_DeactivateSet(InputActionSetHandle handle) { + int64_t index = -1; + auto count = activeInputSetStack.Count(); + for (int64_t i = 0; i < count; ++i) { + if (activeInputSetStack[i] == handle) { + index = i; + break; + } + } + assert(index >= 0 && "PkeInput_DeactivateSet - expected InputActionSet to be active"); + assert(index == activeInputSetStack.Count() - 1 && "PkeInput_UnregisterSet - expected InputActionSet to be the last set active"); + InputActionSetHandle_T handleIndex{static_cast<InputActionSetHandle_T>(handle)}; + auto &set = registeredInputSets[handleIndex]; + for (int64_t i = set.actionCount - 1; i >= 0; --i) { + PkeInputAction &action = set.actions[i]; + // TODO doesn't support multiple masks - how do we align scroll + button + // WITHOUT having two events allocated? + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { + registeredCursorEnterEvents.Remove(action.eventIndex); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { + registeredCursorPosEvents.Remove(action.eventIndex); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { + registeredKeyEvents.Remove(action.eventIndex); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { + registeredMouseButtonEvents.Remove(action.eventIndex); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { + registeredScrollEvents.Remove(action.eventIndex); + } + if ((action.primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { + registeredWindowFocusEvents.Remove(action.eventIndex); + } + } + activeInputSetStack.Pop(); +} + +void PkeInput_UnregisterSet(InputActionSetHandle handle) { + InputActionSetHandle_T index{static_cast<InputActionSetHandle_T>(handle)}; + assert(index == registeredInputSets.Count() - 1 && "PkeInput_UnregisterSet - expected InputActionSet to be the last set registered"); + const auto &set = registeredInputSets[index]; + if (activeInputSetStack.Has(handle)) { + PkeInput_DeactivateSet(handle); + } + Pke_Delete<PkeInputAction>(set.actions, set.actionCount); + registeredInputSets.Remove(index); +} + void PkeInput_Teardown() { - UnhandledPkeInputEvents.~DynArray(); - if (prevWindowFocusCallback) { glfwSetWindowFocusCallback(window, prevWindowFocusCallback); } - if (prevScrollCallback) { glfwSetScrollCallback(window, prevScrollCallback); } - if (prevMouseButtonCallback) { glfwSetMouseButtonCallback(window, prevMouseButtonCallback); } - if (prevKeyCallback) { glfwSetKeyCallback(window, prevKeyCallback); } - if (prevCursorPosCallback) { glfwSetCursorPosCallback(window, prevCursorPosCallback); } - if (prevCursorEnterCallback) { glfwSetCursorEnterCallback(window, prevCursorEnterCallback); } + glfwSetWindowFocusCallback(window, prevWindowFocusCallback); + glfwSetScrollCallback(window, prevScrollCallback); + glfwSetMouseButtonCallback(window, prevMouseButtonCallback); + glfwSetKeyCallback(window, prevKeyCallback); + glfwSetCursorPosCallback(window, prevCursorPosCallback); + glfwSetCursorEnterCallback(window, prevCursorEnterCallback); + unhandledCursorPosEvents.~DynArray(); + unhandledCursorEnterEvents.~DynArray(); + unhandledKeyEvents.~DynArray(); + unhandledMouseButtonEvents.~DynArray(); + unhandledScrollEvents.~DynArray(); + unhandledWindowFocusEvents.~DynArray(); + registeredCursorEnterEvents.~DynArray(); + registeredCursorPosEvents.~DynArray(); + registeredKeyEvents.~DynArray(); + registeredMouseButtonEvents.~DynArray(); + registeredScrollEvents.~DynArray(); + registeredWindowFocusEvents.~DynArray(); + activeInputSetStack.~DynArray(); + registeredInputSets.~DynArray(); } |
