#include "player-input.hpp" #include "pk.h" #include "window.hpp" #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); pk_arr_t unhandledCursorPosEvents{}; pk_arr_t unhandledCursorEnterEvents{}; pk_arr_t unhandledKeyEvents{}; pk_arr_t unhandledMouseButtonEvents{}; pk_arr_t unhandledScrollEvents{}; pk_arr_t unhandledWindowFocusEvents{}; pk_arr_t registeredCursorEnterEvents{}; bool lastCursorEntered = false; pk_arr_t registeredCursorPosEvents{}; struct { double x = 0; double y = 0; } lastMousePos; pk_arr_t registeredKeyEvents{}; pk_arr_t registeredMouseButtonEvents{}; pk_arr_t registeredScrollEvents{}; pk_arr_t registeredWindowFocusEvents{}; bool lastWindowFocus = false; pk_arr_t registeredInputSets{}; pk_arr_t activeInputSetStack{}; PkeInputAction *FindActionByName(const char *actionName) { uint32_t count = activeInputSetStack.next; for (uint32_t i = count; i > 0; --i) { InputActionSetHandle handle = activeInputSetStack[i-1]; 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, int32_t index) { activeEvent = nullptr; if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { activeEvent = ®isteredCursorEnterEvents[inputAction->event_indices[index]]; return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { activeEvent = ®isteredCursorPosEvents[inputAction->event_indices[index]]; return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { activeEvent = ®isteredKeyEvents[inputAction->event_indices[index]]; return PKE_INPUT_HASH_EVENT_TYPE_KEY; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { activeEvent = ®isteredMouseButtonEvents[inputAction->event_indices[index]]; return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { activeEvent = ®isteredScrollEvents[inputAction->event_indices[index]]; return PKE_INPUT_HASH_EVENT_TYPE_SCROLL; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { activeEvent = ®isteredWindowFocusEvents[inputAction->event_indices[index]]; 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.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { any = any || bool(registeredCursorPosEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { any = any || bool(registeredKeyEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { any = any || bool(registeredMouseButtonEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { any = any || bool(registeredScrollEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { any = any || bool(registeredWindowFocusEvents.next); } if (any == false) return InputEventHash{0}; uint32_t count = activeInputSetStack.next; for (uint32_t i = count; i > 0; --i) { InputActionSetHandle handle = activeInputSetStack[i-1]; 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) { uint32_t count, i; activeEvent = nullptr; bool any = false; if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { any = any || bool(registeredCursorEnterEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { any = any || bool(registeredCursorPosEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { any = any || bool(registeredKeyEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { any = any || bool(registeredMouseButtonEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { any = any || bool(registeredScrollEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { any = any || bool(registeredWindowFocusEvents.next); } if (any == false) return InputEventHash{0}; count = activeInputSetStack.next; for (i = count; i > 0; --i) { InputActionSetHandle handle = activeInputSetStack[i-1]; 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_INDEX_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.event_indices[l]]; return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { activeEvent = ®isteredCursorPosEvents[inputAction.event_indices[l]]; return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { activeEvent = ®isteredKeyEvents[inputAction.event_indices[l]]; return PKE_INPUT_HASH_EVENT_TYPE_KEY; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { activeEvent = ®isteredMouseButtonEvents[inputAction.event_indices[l]]; return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { activeEvent = ®isteredScrollEvents[inputAction.event_indices[l]]; return PKE_INPUT_HASH_EVENT_TYPE_SCROLL; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { activeEvent = ®isteredWindowFocusEvents[inputAction.event_indices[l]]; 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) { (void)delta; uint32_t count, i; // reset everything // @performance could happen concurrently { count = registeredCursorPosEvents.next; for (i = 0; i < count; ++i) { auto &ev = registeredCursorPosEvents[i]; ev.xMotion = 0; ev.yMotion = 0; } count = registeredKeyEvents.next; for (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.next; for (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.next; for (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.next-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; } count = unhandledCursorPosEvents.next; for (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) count = unhandledKeyEvents.next; for (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) count = unhandledMouseButtonEvents.next; for (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; } count = unhandledScrollEvents.next; for (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.next-1]; primaryEvent->isFocused = ev.focused; lastWindowFocus = ev.focused; } while (0); } pk_arr_reset(&unhandledCursorEnterEvents); pk_arr_reset(&unhandledCursorPosEvents); pk_arr_reset(&unhandledKeyEvents); pk_arr_reset(&unhandledMouseButtonEvents); pk_arr_reset(&unhandledScrollEvents); pk_arr_reset(&unhandledWindowFocusEvents); // 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 // JCB 2025-05-21 // I'm refactoring this and it seems to skip the last element. // At a glance, I'm not sure if this is intentional or a bug. // I'm going to preserve this logic in my refactor. // Update this comment as needed and make sure to document the purpose. { count = registeredCursorEnterEvents.next; for (i = 0; i < count; ++i) { registeredCursorEnterEvents[i].isEntered = lastCursorEntered; if (i == count-2) break; } count = registeredWindowFocusEvents.next; for (i = 0; i < count; ++i) { registeredWindowFocusEvents[i].isFocused = lastWindowFocus; if (i == count-2) break; } } } PkeInputEventHolder PkeInput_Query(const char *actionName) { PkeInputEventBase *ev = nullptr; auto *action = FindActionByName(actionName); InputEventHash type = InputEventHash{0}; for (int32_t i = 0; i < PKE_INPUT_ACTION_MASK_INDEX_COUNT; ++i) { PkeInputEventMask event_mask = action->masks[i]; if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER)) { PkeCursorEnterEvent *event; type = FindActivePkeInputAction_ByAction(action, event, i); ev = event; break; } else if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS)) { PkeCursorPosEvent *event; type = FindActivePkeInputAction_ByAction(action, event, i); ev = event; break; } else if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY)) { PkeKeyEvent *event; type = FindActivePkeInputAction_ByAction(action, event, i); ev = event; break; } else if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON)) { PkeMouseButtonEvent *event; type = FindActivePkeInputAction_ByAction(action, event, i); ev = event; break; } else if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL)) { PkeScrollEvent *event; type = FindActivePkeInputAction_ByAction(action, event, i); ev = event; break; } else if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS)) { PkeWindowFocusEvent *event; type = FindActivePkeInputAction_ByAction(action, event, i); ev = event; break; } } return PkeInputEventHolder { .type = type, .ptr = ev, }; } void CursorEnterCallback(GLFWwindow *window, int entered) { if (registeredCursorEnterEvents.next) { pk_arr_append_t(&unhandledCursorEnterEvents, { .entered = bool(entered) }); } if (prevCursorEnterCallback) { prevCursorEnterCallback(window, entered); } } void CursorPosCallback(GLFWwindow *window, double xPos, double yPos) { if (registeredCursorPosEvents.next) { pk_arr_append_t(&unhandledCursorPosEvents, { .x = xPos, .y = yPos, }); } if (prevCursorPosCallback) { prevCursorPosCallback(window, xPos, yPos); } } void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { if (registeredKeyEvents.next) { pk_arr_append_t(&unhandledKeyEvents, { .button = key, .mods = mods & 0x0F, .action = int8_t(action), }); } if (prevKeyCallback) { prevKeyCallback(window, key, scancode, action, mods); } } void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) { if (registeredMouseButtonEvents.next) { pk_arr_append_t(&unhandledMouseButtonEvents, { .button = button, .mods = mods, .action = int8_t(action), }); } if (prevMouseButtonCallback) { prevMouseButtonCallback(window, button, action, mods); } } void ScrollCallback(GLFWwindow *window, double xOffset, double yOffset) { if (registeredScrollEvents.next) { pk_arr_append_t(&unhandledScrollEvents, { .x = xOffset, .y = yOffset, }); } if (prevScrollCallback) { prevScrollCallback(window, xOffset, yOffset); } } void WindowFocusCallback(GLFWwindow *window, int focused) { if (registeredWindowFocusEvents.next) { pk_arr_append_t(&unhandledWindowFocusEvents, { .focused = bool(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.next)}; pk_arr_append_t(®isteredInputSets, set); return returnValue; } void PkeInput_ActivateSet(InputActionSetHandle handle) { InputActionSetHandle_T index{static_cast(handle)}; pk_arr_append_t(&activeInputSetStack, handle); auto &set = registeredInputSets[index]; for (int64_t i = 0; i < set.actionCount; ++i) { PkeInputAction &action = set.actions[i]; for (int k = 0; k < PKE_INPUT_ACTION_MASK_INDEX_COUNT; ++k) { PkeInputEventMask event_mask = action.masks[k]; if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { action.event_indices[k] = registeredCursorEnterEvents.next; PkeCursorEnterEvent ev{}; ev.sourceSet = handle; ev.isEntered = lastCursorEntered; pk_arr_append_t(®isteredCursorEnterEvents, ev); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { action.event_indices[k] = registeredCursorPosEvents.next; PkeCursorPosEvent ev {}; ev.sourceSet = handle; ev.xMotion = 0; ev.yMotion = 0; pk_arr_append_t(®isteredCursorPosEvents, ev); glfwGetCursorPos(window, &lastMousePos.x, &lastMousePos.y); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { action.event_indices[k] = registeredKeyEvents.next; PkeKeyEvent ev{}; ev.sourceSet = handle; ev.button = event_mask.button; ev.mods = event_mask.mods; pk_arr_append_t(®isteredKeyEvents, ev); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { action.event_indices[k] = registeredMouseButtonEvents.next; PkeMouseButtonEvent ev{}; ev.sourceSet = handle; ev.button = event_mask.button; ev.mods = event_mask.mods; pk_arr_append_t(®isteredMouseButtonEvents, ev); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { action.event_indices[k] = registeredScrollEvents.next; PkeScrollEvent ev{}; ev.sourceSet = handle; ev.xMotion = 0; ev.yMotion = 0; pk_arr_append_t(®isteredScrollEvents, ev); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { action.event_indices[k] = registeredWindowFocusEvents.next; PkeWindowFocusEvent ev{}; ev.sourceSet = handle; ev.isFocused = lastWindowFocus; pk_arr_append_t(®isteredWindowFocusEvents, ev); } } } } void PkeInput_DeactivateSet(InputActionSetHandle handle) { int64_t index = -1; uint32_t count, i; count = activeInputSetStack.next; for (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.next - 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]; for (int k = 0; k < PKE_INPUT_ACTION_MASK_INDEX_COUNT; ++k) { PkeInputEventMask event_mask = action.masks[k]; if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != InputEventHash{0}) { pk_arr_remove_at(®isteredCursorEnterEvents, action.event_indices[k]); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != InputEventHash{0}) { pk_arr_remove_at(®isteredCursorPosEvents, action.event_indices[k]); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY) != InputEventHash{0}) { pk_arr_remove_at(®isteredKeyEvents, action.event_indices[k]); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != InputEventHash{0}) { pk_arr_remove_at(®isteredMouseButtonEvents, action.event_indices[k]); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != InputEventHash{0}) { pk_arr_remove_at(®isteredScrollEvents, action.event_indices[k]); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != InputEventHash{0}) { pk_arr_remove_at(®isteredWindowFocusEvents, action.event_indices[k]); } } } pk_arr_remove_at(&activeInputSetStack, activeInputSetStack.next-1); } bool PkeInput_pke_arr_find_first_handle(void *search_handle, void *list_handle) { return reinterpret_cast(search_handle) == reinterpret_cast(list_handle); } void PkeInput_UnregisterSet(InputActionSetHandle handle) { InputActionSetHandle_T index{static_cast(handle)}; assert(index == registeredInputSets.next - 1 && "PkeInput_UnregisterSet - expected InputActionSet to be the last set registered"); const auto &set = registeredInputSets[index]; if (pk_arr_find_first_index(&activeInputSetStack, &handle, PkeInput_pke_arr_find_first_handle) != uint32_t(-1)) { PkeInput_DeactivateSet(handle); } pk_delete_arr(set.actions, set.actionCount); pk_arr_remove_at(®isteredInputSets, index); } pk_arr_t &PkeInput_GetInputSets() { return registeredInputSets; } void PkeInput_Teardown() { glfwSetWindowFocusCallback(window, prevWindowFocusCallback); glfwSetScrollCallback(window, prevScrollCallback); glfwSetMouseButtonCallback(window, prevMouseButtonCallback); glfwSetKeyCallback(window, prevKeyCallback); glfwSetCursorPosCallback(window, prevCursorPosCallback); glfwSetCursorEnterCallback(window, prevCursorEnterCallback); pk_arr_reset(&unhandledCursorPosEvents); pk_arr_reset(&unhandledCursorEnterEvents); pk_arr_reset(&unhandledKeyEvents); pk_arr_reset(&unhandledMouseButtonEvents); pk_arr_reset(&unhandledScrollEvents); pk_arr_reset(&unhandledWindowFocusEvents); pk_arr_reset(®isteredCursorEnterEvents); pk_arr_reset(®isteredCursorPosEvents); pk_arr_reset(®isteredKeyEvents); pk_arr_reset(®isteredMouseButtonEvents); pk_arr_reset(®isteredScrollEvents); pk_arr_reset(®isteredWindowFocusEvents); pk_arr_reset(&activeInputSetStack); pk_arr_reset(®isteredInputSets); }