#include "player-input.hpp" #include "pk.h" #include "window.hpp" #include 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_ByName(const char *actionName, S *&activeEvent) { 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}; uint32_t count = activeInputSetStack.next; for (uint32_t i = count; i > 0; --i) { pke_input_action_set_handle handle = activeInputSetStack[i-1]; 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; }; if constexpr((T & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != pke_input_event_hash{0}) { activeEvent = ®isteredCursorEnterEvents[inputAction.eventIndex]; 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.eventIndex]; 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.eventIndex]; 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.eventIndex]; 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.eventIndex]; 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.eventIndex]; 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 count, i; 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}; count = activeInputSetStack.next; for (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]; for (int64_t 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; } } } } 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 count, i; // reset everything // @performance could happen concurrently { count = registeredCursorPosEvents.next; for (i = 0; i < count; ++i) { auto &ev = registeredCursorPosEvents[i]; ev.data.cursor_pos.xMotion = 0; ev.data.cursor_pos.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.data.key.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.data.mouse_button.thisTick = false; } count = registeredScrollEvents.next; for (i = 0; i < count; ++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. 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) continue; 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) continue; 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) continue; 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-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].data.cursor_enter.isEntered = lastCursorEntered; if (i == count-2) break; } count = registeredWindowFocusEvents.next; for (i = 0; i < count; ++i) { registeredWindowFocusEvents[i].data.window_focus.isFocused = lastWindowFocus; if (i == count-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) { if (registeredCursorPosEvents.next) { 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)}; pk_arr_append_t(&activeInputSetStack, handle); auto &set = registeredInputSets[index]; 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_mask event_mask = action.masks[k]; if ((event_mask.computedHash & PKE_INPUT_HASH_EVENT_TYPE_CURSOR_ENTER) != pke_input_event_hash{0}) { action.event_indices[k] = registeredCursorEnterEvents.next; pke_input_event ev{}; ev.sourceSet = handle; 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; pke_input_event ev {}; ev.sourceSet = handle; 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; pke_input_event ev{}; ev.sourceSet = handle; 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; pke_input_event ev{}; ev.sourceSet = handle; 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; pke_input_event ev{}; ev.sourceSet = handle; 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; pke_input_event ev{}; ev.sourceSet = handle; ev.type = PKE_INPUT_HASH_EVENT_TYPE_WINDOW_FOCUS; ev.data.window_focus.isFocused = lastWindowFocus; pk_arr_append_t(®isteredWindowFocusEvents, ev); } } } } 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 == 0) { fprintf(stderr, "[pke_input] Attempt to deactivate a non-active set.\n"); return; } 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_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"); 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) { pk_delete_arr(set.actions, set.actionCount); } pk_arr_remove_at(®isteredInputSets, index); } pk_arr_t &pke_input_get_input_sets() { return registeredInputSets; } 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); }