#include "player-input.hpp" #include "dynamic-array.hpp" #include "game-settings.hpp" #include "window.hpp" #include #include 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; GLFWmousebuttonfun prevMouseButtonCallback; GLFWscrollfun prevScrollCallback; GLFWwindowfocusfun prevWindowFocusCallback; TypeSafeInt_B(InputActionSetHandle); DynArray unhandledCursorPosEvents{512, nullptr}; DynArray unhandledCursorEnterEvents{8, nullptr}; DynArray unhandledKeyEvents{8, nullptr}; DynArray unhandledMouseButtonEvents{8, nullptr}; DynArray unhandledScrollEvents{8, nullptr}; DynArray unhandledWindowFocusEvents{8, nullptr}; DynArray registeredCursorEnterEvents{8, nullptr}; bool lastCursorEntered = false; DynArray registeredCursorPosEvents{8, nullptr}; struct { double x = 0; double y = 0; } lastMousePos; DynArray registeredKeyEvents{8, nullptr}; DynArray registeredMouseButtonEvents{8, nullptr}; DynArray registeredScrollEvents{8, nullptr}; DynArray registeredWindowFocusEvents{8, nullptr}; bool lastWindowFocus = false; // DynArray UnhandledPkeInputEvents{1024, nullptr}; DynArray registeredInputSets{0, nullptr}; DynArray 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(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 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 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(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 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(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; constexpr auto FindActivePkeInputAction_CursorPos_ByType = FindActivePkeInputAction_ByType; constexpr auto FindActivePkeInputAction_Key_ByType = FindActivePkeInputAction_ByType; constexpr auto FindActivePkeInputAction_MouseButton_ByType = FindActivePkeInputAction_ByType; constexpr auto FindActivePkeInputAction_Scroll_ByType = FindActivePkeInputAction_ByType; constexpr auto FindActivePkeInputAction_WindowFocus_ByType = FindActivePkeInputAction_ByType; constexpr auto FindActivePkeInputAction_CursorEnter_ByName = FindActivePkeInputAction_ByName; constexpr auto FindActivePkeInputAction_CursorPos_ByName = FindActivePkeInputAction_ByName; constexpr auto FindActivePkeInputAction_Key_ByName = FindActivePkeInputAction_ByName; constexpr auto FindActivePkeInputAction_MouseButton_ByName = FindActivePkeInputAction_ByName; constexpr auto FindActivePkeInputAction_Scroll_ByName = FindActivePkeInputAction_ByName; constexpr auto FindActivePkeInputAction_WindowFocus_ByName = FindActivePkeInputAction_ByName; void PkeInput_Tick(double delta) { // 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.thisTick = false; } 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.thisTick = false; } 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; primaryEvent->thisTick = true; } else if (ev.action == GLFW_RELEASE) { primaryEvent->isPressed = false; primaryEvent->thisTick = true; } else { // repeat primaryEvent->isPressed = true; } } } 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; primaryEvent->thisTick = true; } else if (ev.action == GLFW_RELEASE) { primaryEvent->isPressed = false; primaryEvent->thisTick = true; } } } 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; } } } 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(action, event); ev = event; } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS)) { PkeCursorPosEvent *event; type = FindActivePkeInputAction_ByAction(action, event); ev = event; } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY)) { PkeKeyEvent *event; type = FindActivePkeInputAction_ByAction(action, event); ev = event; } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON)) { PkeMouseButtonEvent *event; type = FindActivePkeInputAction_ByAction(action, event); ev = event; } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL)) { PkeScrollEvent *event; type = FindActivePkeInputAction_ByAction(action, event); ev = event; } else if (bool(action->primaryHash.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS)) { PkeWindowFocusEvent *event; type = FindActivePkeInputAction_ByAction(action, event); ev = event; } return PkeInputEventHolder { .type = type, .ptr = ev, }; } void CursorEnterCallback(GLFWwindow *window, int entered) { if (registeredCursorEnterEvents.Count()) { auto &ev = unhandledCursorEnterEvents.Push(); ev.entered = bool(entered); } if (prevCursorEnterCallback) { prevCursorEnterCallback(window, entered); } } void CursorPosCallback(GLFWwindow *window, double xPos, double yPos) { 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) { 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) { 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) { 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) { if (registeredWindowFocusEvents.Count()) { auto &ev = unhandledWindowFocusEvents.Push(); ev.focused = focused; } if (prevWindowFocusCallback) { prevWindowFocusCallback(window, focused); } } void PkeInput_Init() { prevCursorEnterCallback = glfwSetCursorEnterCallback(window, CursorEnterCallback); prevCursorPosCallback = glfwSetCursorPosCallback(window, CursorPosCallback); prevKeyCallback = glfwSetKeyCallback(window, KeyCallback); prevMouseButtonCallback = glfwSetMouseButtonCallback(window, MouseButtonCallback); prevScrollCallback = glfwSetScrollCallback(window, ScrollCallback); prevWindowFocusCallback = glfwSetWindowFocusCallback(window, WindowFocusCallback); } InputActionSetHandle PkeInput_RegisterSet(const PkeInputSet &set) { InputActionSetHandle returnValue{static_cast(registeredInputSets.Count())}; registeredInputSets.Push(set); return returnValue; } void PkeInput_ActivateSet(InputActionSetHandle handle) { InputActionSetHandle_T index{static_cast(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(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(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); } pk_delete(set.actions, set.actionCount); registeredInputSets.Remove(index); } void PkeInput_Teardown() { 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(); }