#include "player-input.hpp" #include "pk.h" #include "window.hpp" #include /* pke_input is weird. * We are trying to enforce FIFO for both: * - activate/deactivate * - register/unregister * However, this may or may not be practical (for each individually). * If this does becomes impractical, I believe the solution will be to convert * most of this file to pk_bkt_arr's. * However, this will cause some annoyances with precedence. * Currently, a `pke_input_action` tracks its event index when activated. * Currently, When searching for the most recently registered input, we * assume the last is the most recent. * If we switch to a pk_bkt_arr, then the "newest" item won't be at the end * and will require a new method to find the most-recent. * If we stick with arrays and an object in the middle is removed, an * extensive search would be required to fix all proceeding indexes. */ struct pke_input_unhandled_event { pke_input_event_hash type; union pke_input_unhandled_event_data { struct pke_input_unhandled_event_cursor_enter { bool entered; } cursor_enter; struct pke_input_unhandled_event_cursor_pos { double x; double y; } cursor_pos; struct pke_input_unhandled_event_key { int32_t button; int32_t mods; int8_t action; } key; struct pke_input_unhandled_event_mouse_button { int32_t button; int32_t mods; int8_t action; } mouse_button; struct pke_input_unhandled_event_scroll { double x; double y; } scroll; struct pke_input_unhandled_event_window_focus { bool focused; } window_focus; } data; }; GLFWcursorenterfun prevCursorEnterCallback; GLFWcursorposfun prevCursorPosCallback; GLFWkeyfun prevKeyCallback; GLFWmousebuttonfun prevMouseButtonCallback; GLFWscrollfun prevScrollCallback; GLFWwindowfocusfun prevWindowFocusCallback; TypeSafeInt_B(pke_input_action_set_handle); pk_arr_t unhandled_events; 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{}; pke_input_action *FindActionByName(const char *actionName) { uint32_t count = activeInputSetStack.next; for (uint32_t i = count; i > 0; --i) { pke_input_action_set_handle handle = activeInputSetStack[i-1]; if (handle == pke_input_action_set_handle_MAX) continue; pke_input_action_set_handle_T index = static_cast(handle); pke_input_set &set = registeredInputSets[index]; for (int64_t k = 0; k < set.actionCount; ++k) { pke_input_action &inputAction = set.actions[k]; if (strcmp(actionName, inputAction.name) != 0) { continue; }; return &inputAction; } } return nullptr; } template pke_input_event_hash FindActivepke_input_action_ByAction(const pke_input_action *inputAction, S *&activeEvent, int32_t index) { activeEvent = nullptr; if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != pke_input_event_hash{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) != pke_input_event_hash{0}) { activeEvent = ®isteredCursorPosEvents[inputAction->event_indices[index]]; return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != pke_input_event_hash{0}) { activeEvent = ®isteredKeyEvents[inputAction->event_indices[index]]; return PKE_INPUT_HASH_EVENT_TYPE_KEY; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != pke_input_event_hash{0}) { activeEvent = ®isteredMouseButtonEvents[inputAction->event_indices[index]]; return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != pke_input_event_hash{0}) { activeEvent = ®isteredScrollEvents[inputAction->event_indices[index]]; return PKE_INPUT_HASH_EVENT_TYPE_SCROLL; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != pke_input_event_hash{0}) { activeEvent = ®isteredWindowFocusEvents[inputAction->event_indices[index]]; return PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS; } return pke_input_event_hash{0}; } template pke_input_event_hash FindActivepke_input_action_ByType(const pke_input_event_mask &mask, pke_input_event *&activeEvent) { uint32_t i; int64_t k,l; activeEvent = nullptr; bool any = false; if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != pke_input_event_hash{0}) { any = any || bool(registeredCursorEnterEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != pke_input_event_hash{0}) { any = any || bool(registeredCursorPosEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != pke_input_event_hash{0}) { any = any || bool(registeredKeyEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != pke_input_event_hash{0}) { any = any || bool(registeredMouseButtonEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != pke_input_event_hash{0}) { any = any || bool(registeredScrollEvents.next); } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != pke_input_event_hash{0}) { any = any || bool(registeredWindowFocusEvents.next); } if (any == false) return pke_input_event_hash{0}; for (i = activeInputSetStack.next; i > 0; --i) { pke_input_action_set_handle handle = activeInputSetStack[i-1]; if (handle == pke_input_action_set_handle_MAX) continue; pke_input_action_set_handle_T index = static_cast(handle); pke_input_set &set = registeredInputSets[index]; for (k = 0; k < set.actionCount; ++k) { pke_input_action &inputAction = set.actions[k]; for (l = 0; l < PKE_INPUT_ACTION_MASK_INDEX_COUNT; ++l) { pke_input_event_mask &evMask = inputAction.masks[l]; if ((evMask.computedHash & mask.computedHash) == pke_input_event_hash{0}) { continue; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != pke_input_event_hash{0}) { if (evMask.button != mask.button) { continue; } if (evMask.mods != mask.mods) { continue; } } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != pke_input_event_hash{0}) { if (evMask.button != mask.button) { continue; } if (evMask.mods != mask.mods) { continue; } } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != pke_input_event_hash{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) != pke_input_event_hash{0}) { activeEvent = ®isteredCursorPosEvents[inputAction.event_indices[l]]; return PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_KEY) != pke_input_event_hash{0}) { activeEvent = ®isteredKeyEvents[inputAction.event_indices[l]]; return PKE_INPUT_HASH_EVENT_TYPE_KEY; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != pke_input_event_hash{0}) { activeEvent = ®isteredMouseButtonEvents[inputAction.event_indices[l]]; return PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != pke_input_event_hash{0}) { activeEvent = ®isteredScrollEvents[inputAction.event_indices[l]]; return PKE_INPUT_HASH_EVENT_TYPE_SCROLL; } if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != pke_input_event_hash{0}) { activeEvent = ®isteredWindowFocusEvents[inputAction.event_indices[l]]; return PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS; } } } } fprintf(stderr, "[pke_input][FindActivepke_input_action_ByType] hash: %hu had events but found 0.\n", static_cast(T)); return pke_input_event_hash{0}; } constexpr auto FindActivepke_input_action_CursorEnter_ByType = FindActivepke_input_action_ByType; constexpr auto FindActivepke_input_action_CursorPos_ByType = FindActivepke_input_action_ByType; constexpr auto FindActivepke_input_action_Key_ByType = FindActivepke_input_action_ByType; constexpr auto FindActivepke_input_action_MouseButton_ByType = FindActivepke_input_action_ByType; constexpr auto FindActivepke_input_action_Scroll_ByType = FindActivepke_input_action_ByType; constexpr auto FindActivepke_input_action_WindowFocus_ByType = FindActivepke_input_action_ByType; // constexpr auto FindActivepke_input_action_CursorEnter_ByName = FindActivepke_input_action_ByName; // constexpr auto FindActivepke_input_action_CursorPos_ByName = FindActivepke_input_action_ByName; // constexpr auto FindActivepke_input_action_Key_ByName = FindActivepke_input_action_ByName; // constexpr auto FindActivepke_input_action_MouseButton_ByName = FindActivepke_input_action_ByName; // constexpr auto FindActivepke_input_action_Scroll_ByName = FindActivepke_input_action_ByName; // constexpr auto FindActivepke_input_action_WindowFocus_ByName = FindActivepke_input_action_ByName; void pke_input_tick(double delta) { (void)delta; uint32_t i; // reset everything // @performance could happen concurrently { for (i = 0; i < registeredCursorPosEvents.next; ++i) { auto &ev = registeredCursorPosEvents[i]; ev.data.cursor_pos.xMotion = 0; ev.data.cursor_pos.yMotion = 0; } for (i = 0; i < registeredKeyEvents.next; ++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.data.key.thisTick = false; } for (i = 0; i < registeredMouseButtonEvents.next; ++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.data.mouse_button.thisTick = false; } for (i = 0; i < registeredScrollEvents.next; ++i) { auto &ev = registeredScrollEvents[i]; ev.data.scroll.xMotion = 0; ev.data.scroll.yMotion = 0; } } // handle unhandled events // @performance cache action->event results // 2025-07-23 JCB I'm not sure what this perf message means. // 2025-10-01 JCB // It means hold on to the "last" event per-hash so we don't // have to keep looking it up. // Might be tricky for keys, but for all other events we could hold a global // pointer, updated on register/unregister; no more searching. // A Dictionary/Map would be a good candidate, but we do not currently have a // performant option. pke_input_event *primary_event = nullptr; for (i = 0; i < unhandled_events.next; ++i) { pke_input_unhandled_event &uh_ev = unhandled_events[i]; switch (uh_ev.type) { case PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER: FindActivepke_input_action_CursorEnter_ByType(pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_CURSOR_ENTER_EVENTS }, primary_event); if (primary_event != nullptr) { primary_event->data.cursor_enter.isEntered = uh_ev.data.cursor_enter.entered; } lastCursorEntered = uh_ev.data.cursor_enter.entered; break; case PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS: FindActivepke_input_action_CursorPos_ByType(pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_CURSOR_POS_EVENTS }, primary_event); if (primary_event != nullptr) { primary_event->data.cursor_pos.xMotion += uh_ev.data.cursor_pos.x - lastMousePos.x; primary_event->data.cursor_pos.yMotion += uh_ev.data.cursor_pos.y - lastMousePos.y; } lastMousePos.x = uh_ev.data.cursor_pos.x; lastMousePos.y = uh_ev.data.cursor_pos.y; break; case PKE_INPUT_HASH_EVENT_TYPE_KEY: FindActivepke_input_action_Key_ByType(pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_KEY_EVENTS, .button = uh_ev.data.key.button, .mods = uh_ev.data.key.mods, }, primary_event); if (primary_event == nullptr) continue; if (uh_ev.data.key.action == GLFW_PRESS) { primary_event->data.key.isPressed = true; primary_event->data.key.thisTick = true; } else if (uh_ev.data.key.action == GLFW_RELEASE) { primary_event->data.key.isPressed = false; primary_event->data.key.thisTick = true; } else { // repeat primary_event->data.key.isPressed = true; } break; case PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON: FindActivepke_input_action_MouseButton_ByType(pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_MOUSE_BUTTON_EVENTS, .button = uh_ev.data.mouse_button.button, .mods = uh_ev.data.mouse_button.mods, }, primary_event); if (primary_event == nullptr) continue; if (uh_ev.data.mouse_button.action == GLFW_PRESS) { primary_event->data.mouse_button.isPressed = true; primary_event->data.mouse_button.thisTick = true; } else if (uh_ev.data.mouse_button.action == GLFW_RELEASE) { primary_event->data.mouse_button.isPressed = false; primary_event->data.mouse_button.thisTick = true; } break; case PKE_INPUT_HASH_EVENT_TYPE_SCROLL: FindActivepke_input_action_Scroll_ByType(pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_SCROLL_EVENTS }, primary_event); if (primary_event == nullptr) continue; primary_event->data.scroll.xMotion += uh_ev.data.scroll.x; primary_event->data.scroll.yMotion += uh_ev.data.scroll.y; break; case PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS: FindActivepke_input_action_WindowFocus_ByType(pke_input_event_mask { .computedHash = PKE_INPUT_HASH_ALL_WINDOW_FOCUS_EVENTS }, primary_event); if (primary_event != nullptr) { primary_event->data.window_focus.isFocused = uh_ev.data.window_focus.focused; } lastWindowFocus = uh_ev.data.window_focus.focused; break; default: fprintf(stderr, "[pke_input_tick] Unknown \"unhandled input\" type: %i", pke_input_event_hash_T(uh_ev.type)); break; } } pk_arr_reset(&unhandled_events); // 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-10-01 // Skipping the last element is intentional. // The "last" element was already handled in the above logic. // Consider testing handling this on un-register instead. { for (i = 0; i < registeredCursorEnterEvents.next; ++i) { registeredCursorEnterEvents[i].data.cursor_enter.isEntered = lastCursorEntered; if (i == registeredCursorEnterEvents.next-2) break; } for (i = 0; i < registeredWindowFocusEvents.next; ++i) { registeredWindowFocusEvents[i].data.window_focus.isFocused = lastWindowFocus; if (i == registeredWindowFocusEvents.next-2) break; } } } const pke_input_event *pke_input_query_by_action_name(const char *actionName) { pke_input_event *ev = nullptr; auto *action = FindActionByName(actionName); if (action == nullptr) return nullptr; for (int32_t i = 0; i < PKE_INPUT_ACTION_MASK_INDEX_COUNT; ++i) { pke_input_event_mask event_mask = action->masks[i]; if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER)) { FindActivepke_input_action_ByAction(action, ev, i); break; } else if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS)) { FindActivepke_input_action_ByAction(action, ev, i); break; } else if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY)) { FindActivepke_input_action_ByAction(action, ev, i); break; } else if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON)) { FindActivepke_input_action_ByAction(action, ev, i); break; } else if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL)) { FindActivepke_input_action_ByAction(action, ev, i); break; } else if (bool(event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS)) { FindActivepke_input_action_ByAction(action, ev, i); break; } } return ev; } void pke_input_query_by_mask(pke_input_event_mask mask, pke_input_event &ev) { pke_input_event_hash type = mask.computedHash & PKE_INPUT_HASH_ALL_EVENTS; switch (type) { case PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER: /* no-op */ break; case PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS: /* no-op */ break; case PKE_INPUT_HASH_EVENT_TYPE_KEY: ev.type = PKE_INPUT_HASH_EVENT_TYPE_KEY; ev.sourceSet = pke_input_action_set_handle_MAX; ev.data.key.button = mask.button; ev.data.key.isPressed = bool(glfwGetKey(window, mask.button)); ev.data.key.mods = 0; ev.data.key.thisTick = false; break; case PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON: ev.type = PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; ev.sourceSet = pke_input_action_set_handle_MAX; ev.data.mouse_button.button = mask.button; ev.data.mouse_button.isPressed = bool(glfwGetMouseButton(window, mask.button)); ev.data.mouse_button.mods = 0; ev.data.mouse_button.thisTick = false; break; case PKE_INPUT_HASH_EVENT_TYPE_SCROLL: /* no-op */ break; case PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS: /* no-op */ break; default: fprintf(stderr, "[pke_input_query_by_mask] unhandled input type: %i\n", pke_input_event_hash_T(type)); break; } return; } void pke_input_query_mouse_pos(double &x, double &y) { x = lastMousePos.x; y = lastMousePos.y; } void CursorEnterCallback(GLFWwindow *window, int entered) { if (registeredCursorEnterEvents.next) { pk_arr_append_t(&unhandled_events, { .type = PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER, .data = { .cursor_enter = { .entered = bool(entered) } } }); } if (prevCursorEnterCallback) { prevCursorEnterCallback(window, entered); } } void CursorPosCallback(GLFWwindow *window, double xPos, double yPos) { // always handle cursor pos. Used by UI. pk_arr_append_t(&unhandled_events, { .type = PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS, .data = { .cursor_pos = { .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(&unhandled_events, { .type = PKE_INPUT_HASH_EVENT_TYPE_KEY, .data = { .key = { .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(&unhandled_events, { .type = PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON, .data = { .mouse_button = { .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(&unhandled_events, { .type = PKE_INPUT_HASH_EVENT_TYPE_SCROLL, .data = { .scroll = { .x = xOffset, .y = yOffset, }, }, }); } if (prevScrollCallback) { prevScrollCallback(window, xOffset, yOffset); } } void WindowFocusCallback(GLFWwindow *window, int focused) { if (registeredWindowFocusEvents.next) { pk_arr_append_t(&unhandled_events, { .type = PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS, .data = { .window_focus = { .focused = bool(focused), }, }, }); } if (prevWindowFocusCallback) { prevWindowFocusCallback(window, focused); } } void pke_input_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); } pke_input_action_set_handle pke_input_register_set(const pke_input_set &&set) { pke_input_action_set_handle returnValue{static_cast(registeredInputSets.next)}; pk_arr_append_t(®isteredInputSets, set); return returnValue; } void pke_input_activate_set(pke_input_action_set_handle handle) { pke_input_action_set_handle_T index{static_cast(handle)}; int64_t found_index = -1; uint32_t before_cursor_enter_count = registeredCursorEnterEvents.next; uint32_t before_cursor_pos_count = registeredCursorPosEvents.next; uint32_t before_key_ev_count = registeredKeyEvents.next; uint32_t before_mb_ev_count = registeredMouseButtonEvents.next; uint32_t before_scroll_ev_count = registeredScrollEvents.next; uint32_t before_win_focus_count = registeredWindowFocusEvents.next; uint32_t i; for (i = 0; i < activeInputSetStack.next; ++i) { if (activeInputSetStack[i] == handle) { found_index = (int64_t)i; break; } } if (found_index != -1) { fprintf(stderr, "[pke_input] Attempt to activate an already active set!\n"); return; } pk_arr_append_t(&activeInputSetStack, handle); auto &set = registeredInputSets[index]; fprintf(stdout, "[pke_input] Activating set %hhu \"%s\"\n", static_cast(handle), set.title); for (int64_t i = 0; i < set.actionCount; ++i) { pke_input_action &action = set.actions[i]; for (int k = 0; k < PKE_INPUT_ACTION_MASK_INDEX_COUNT; ++k) { pke_input_event ev {}; ev.sourceSet = handle; pke_input_event_mask event_mask = action.masks[k]; if (event_mask.computedHash == pke_input_event_hash{0}) { continue; } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != pke_input_event_hash{0}) { action.event_indices[k] = registeredCursorEnterEvents.next; ev.type = PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER; ev.data.cursor_enter.isEntered = lastCursorEntered; pk_arr_append_t(®isteredCursorEnterEvents, ev); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != pke_input_event_hash{0}) { action.event_indices[k] = registeredCursorPosEvents.next; ev.type = PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS; ev.data.cursor_pos.xMotion = 0; ev.data.cursor_pos.yMotion = 0; pk_arr_append_t(®isteredCursorPosEvents, ev); glfwGetCursorPos(window, &lastMousePos.x, &lastMousePos.y); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY) != pke_input_event_hash{0}) { action.event_indices[k] = registeredKeyEvents.next; ev.type = PKE_INPUT_HASH_EVENT_TYPE_KEY; ev.data.key.button = event_mask.button; ev.data.key.mods = event_mask.mods; pk_arr_append_t(®isteredKeyEvents, ev); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != pke_input_event_hash{0}) { action.event_indices[k] = registeredMouseButtonEvents.next; ev.type = PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON; ev.data.mouse_button.button = event_mask.button; ev.data.mouse_button.mods = event_mask.mods; pk_arr_append_t(®isteredMouseButtonEvents, ev); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != pke_input_event_hash{0}) { action.event_indices[k] = registeredScrollEvents.next; ev.type = PKE_INPUT_HASH_EVENT_TYPE_SCROLL; ev.data.scroll.xMotion = 0; ev.data.scroll.yMotion = 0; pk_arr_append_t(®isteredScrollEvents, ev); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != pke_input_event_hash{0}) { action.event_indices[k] = registeredWindowFocusEvents.next; ev.type = PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS; ev.data.window_focus.isFocused = lastWindowFocus; pk_arr_append_t(®isteredWindowFocusEvents, ev); } } } fprintf(stdout, "[pke_event] activate set %s change: %u\n", "registeredCursorEnterEvents", registeredCursorEnterEvents.next - before_cursor_enter_count); fprintf(stdout, "[pke_event] activate set %s change: %u\n", "registeredCursorPosEvents", registeredCursorPosEvents.next - before_cursor_pos_count); fprintf(stdout, "[pke_event] activate set %s change: %u\n", "registeredKeyEvents", registeredKeyEvents.next - before_key_ev_count); fprintf(stdout, "[pke_event] activate set %s change: %u\n", "registeredMouseButtonEvents", registeredMouseButtonEvents.next - before_mb_ev_count); fprintf(stdout, "[pke_event] activate set %s change: %u\n", "registeredScrollEvents", registeredScrollEvents.next - before_scroll_ev_count); fprintf(stdout, "[pke_event] activate set %s change: %u\n", "registeredWindowFocusEvents", registeredWindowFocusEvents.next - before_win_focus_count); } void pke_input_deactivate_set(pke_input_action_set_handle 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; } } if (index == -1) { fprintf(stderr, "[pke_input] Attempt to deactivate a non-active set.\n"); return; } if (index != activeInputSetStack.next-1) { assert(false && "[pke_input] Attempt to deactivate an active set that was not the last activated (FIFO enforcement).\n"); } pke_input_action_set_handle_T handleIndex{static_cast(handle)}; auto &set = registeredInputSets[handleIndex]; for (int64_t i = set.actionCount - 1; i >= 0; --i) { pke_input_action &action = set.actions[i]; for (int k = 0; k < PKE_INPUT_ACTION_MASK_INDEX_COUNT; ++k) { pke_input_event_mask event_mask = action.masks[k]; if (event_mask.computedHash == pke_input_event_hash{0}) { continue; } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != pke_input_event_hash{0}) { pk_arr_remove_at(®isteredCursorEnterEvents, action.event_indices[k]); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_POS) != pke_input_event_hash{0}) { pk_arr_remove_at(®isteredCursorPosEvents, action.event_indices[k]); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_KEY) != pke_input_event_hash{0}) { pk_arr_remove_at(®isteredKeyEvents, action.event_indices[k]); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_MOUSE_BUTTON) != pke_input_event_hash{0}) { pk_arr_remove_at(®isteredMouseButtonEvents, action.event_indices[k]); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_SCROLL) != pke_input_event_hash{0}) { pk_arr_remove_at(®isteredScrollEvents, action.event_indices[k]); } if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS) != pke_input_event_hash{0}) { pk_arr_remove_at(®isteredWindowFocusEvents, action.event_indices[k]); } } } pk_arr_remove_at(&activeInputSetStack, index); } bool PkeInput_pke_arr_find_first_handle(void *search_handle, void *list_handle) { return reinterpret_cast(search_handle) == reinterpret_cast(list_handle); } void pke_input_unregister_set(pke_input_action_set_handle handle) { pke_input_action_set_handle_T index{static_cast(handle)}; assert(index == registeredInputSets.next - 1 && "pke_input_unregister_set - expected InputActionSet to be the last set registered (fifo enforcement)"); const auto &set = registeredInputSets[index]; if (pk_arr_find_first_index(&activeInputSetStack, &handle, PkeInput_pke_arr_find_first_handle) != uint32_t(-1)) { pke_input_deactivate_set(handle); } if (set.actions != nullptr && set.bkt != nullptr) { pk_delete_arr(set.actions, set.actionCount, set.bkt); } pk_arr_remove_at(®isteredInputSets, index); } pk_arr_t &pke_input_get_input_sets() { return registeredInputSets; } pk_arr_t &pke_input_get_active_input_action_set_handles() { return activeInputSetStack; } void pke_input_teardown() { glfwSetWindowFocusCallback(window, prevWindowFocusCallback); glfwSetScrollCallback(window, prevScrollCallback); glfwSetMouseButtonCallback(window, prevMouseButtonCallback); glfwSetKeyCallback(window, prevKeyCallback); glfwSetCursorPosCallback(window, prevCursorPosCallback); glfwSetCursorEnterCallback(window, prevCursorEnterCallback); pk_arr_reset(&unhandled_events); 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); }