From 177affce9937ee43b67fc5b24f09244d12cf9166 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Wed, 25 Sep 2024 21:16:50 +0200 Subject: [PATCH] Simplified input handling AND prepared for contontroller usage by merging all input states --- input/Input.h | 355 ++++++++++++++++---------------- platform/win32/input/RawInput.h | 281 ++++++++++++++----------- platform/win32/input/XInput.h | 29 +++ stdlib/simd/SIMD_I8.h | 114 +++++++++- 4 files changed, 473 insertions(+), 306 deletions(-) diff --git a/input/Input.h b/input/Input.h index 72ca708..65c33b6 100644 --- a/input/Input.h +++ b/input/Input.h @@ -9,19 +9,17 @@ #ifndef TOS_INPUT_H #define TOS_INPUT_H -// @question Consider to change mouse to secondary input device and keyboard to primary input device and also rename the functions etc. - // How many concurrent mouse/secondary input device presses to we recognize #define MAX_MOUSE_PRESSES 3 // How many concurrent primary key/button presses can be handled? #define MAX_KEY_PRESSES 5 +#define MAX_KEY_STATES (2 * MAX_KEY_PRESSES) -// How many keys/buttons do we support for the primary input device +// How many keys/buttons do we support for the devices #define MAX_KEYBOARD_KEYS 255 - -// How many mouse/secondary input device keys/buttons do we support -#define MAX_MOUSE_KEYS 5 +#define MAX_MOUSE_KEYS 10 +#define MAX_CONTROLLER_KEYS 24 #define MIN_INPUT_DEVICES 2 @@ -33,10 +31,12 @@ // These values are used as bit flags to hint if a "key" is a keyboard/primary or mouse/secondary input // When adding a keybind the "key" can only be uint8 but we expand it to an int and set the first bit accordingly -#define INPUT_KEYBOARD_PREFIX 80000000 #define INPUT_MOUSE_PREFIX 0 +#define INPUT_KEYBOARD_PREFIX 16384 +#define INPUT_CONTROLLER_PREFIX 32768 #define INPUT_TYPE_MOUSE_KEYBOARD 0x01 +#define INPUT_TYPE_CONTROLLER 0x02 #define INPUT_TYPE_OTHER 0x03 #define MIN_CONTROLLER_DEVICES 4 @@ -54,150 +54,179 @@ struct InputMapping { // A key/button can be bound to up to 5 different hotkeys // This is used to check if a key/button has a hotkey association - uint8 keys[MAX_KEYBOARD_KEYS + MAX_MOUSE_KEYS][MAX_KEY_TO_HOTKEY]; + uint8 keys[MAX_MOUSE_KEYS + MAX_KEYBOARD_KEYS + MAX_CONTROLLER_KEYS][MAX_KEY_TO_HOTKEY]; // A hotkey can be bound to a combination of up to 3 key/button presses uint8 hotkey_count; - uint8* hotkeys; + uint16* hotkeys; +}; + +enum KeyState { + KEY_STATE_PRESSED, + KEY_STATE_HELD, + KEY_STATE_RELEASED, +}; + +struct InputKey { + // Includes flag for mouse, keyboard, controller + uint16 key_id; + uint16 key_state; + uint16 value; // e.g. stick/trigger keys + uint64 time; // when was this action performed (useful to decide if key state is held vs pressed) }; // @question Maybe we should also add a third key_down array for controllers and some special controller functions here to just handle everything in one struct // Or think about completely splitting all states (mouse, keyboard, other) struct InputState { // State of the hotkeys, resulting from the device input - // @question maybe create a separate define and make it a little bit larger? uint8 state_hotkeys[MAX_KEY_PRESSES]; - uint8 keys_down[MAX_KEY_PRESSES]; - - // @question Why do we even need this? shouldn't we only care about the current keys down? - uint8 keys_up[MAX_KEY_PRESSES]; - - uint32 mouse_down; + InputKey state_keys[MAX_KEY_STATES]; int32 dx; int32 dy; uint32 x; uint32 y; - - int16 wheel_delta = 0; - int16 hwheel_delta = 0; - - uint64 keys_down_time[MAX_MOUSE_PRESSES + MAX_KEY_PRESSES]; }; struct Input { // Device bool is_connected = false; - byte type = INPUT_TYPE_OTHER; #ifdef _WIN32 // @todo maybe replace with id?! // -> remove _WIN32 section HANDLE handle_keyboard; HANDLE handle_mouse; + HANDLE handle_controller; #endif bool state_change_button = false; bool state_change_mouse = false; - bool state_change_mouse_button = true; bool mouse_movement; InputState state; InputMapping input_mapping; - - // @todo we probably don't need this - InputState state_old; -}; - -struct ControllerInput { - uint32 id = 0; - bool is_connected = false; - - // After handling the state change the game loop should set this to false - bool state_change = false; - - // @question maybe make part of button - bool up = false; - bool down = false; - bool left = false; - bool right = false; - - byte trigger_old[4]; - byte trigger[4]; - - // these are bitfields - uint16 button_old; - uint16 button; - - int16 stickl_x = 0; - int16 stickl_y = 0; - bool stickl_press = false; - - int16 stickr_x = 0; - int16 stickr_y = 0; - bool stickr_press = false; }; inline -void mouse_backup_state(Input* input) +void input_clean_state(InputState* state) { - input->state_old.mouse_down = input->state.mouse_down; - - input->state_old.x = input->state.x; - input->state_old.y = input->state.y; - - input->state_old.wheel_delta = input->state.wheel_delta; - input->state_old.hwheel_delta = input->state.wheel_delta; + for (int i = 0; i < MAX_KEY_STATES; ++i) { + if (state->state_keys[i].key_state == KEY_STATE_RELEASED) { + state->state_keys[i].key_id = 0; + } + } } inline -void keyboard_clean_state(InputState* state) +bool input_action_exists(const InputState* state, uint16 key) { - memset(state->keys_down, 0, MAX_KEY_PRESSES * sizeof(uint8)); - memset(state->keys_up, 0, MAX_KEY_PRESSES * sizeof(uint8)); - memset(state->keys_down_time, 0, (MAX_MOUSE_PRESSES + MAX_KEY_PRESSES) * sizeof(uint64)); + return state->state_keys[0].key_id == key + || state->state_keys[1].key_id == key + || state->state_keys[2].key_id == key + || state->state_keys[3].key_id == key + || state->state_keys[4].key_id == key + || state->state_keys[4].key_id == key + || state->state_keys[5].key_id == key + || state->state_keys[6].key_id == key + || state->state_keys[7].key_id == key + || state->state_keys[8].key_id == key + || state->state_keys[9].key_id == key; } inline -void keyboard_backup_state(Input* input) +bool input_is_down(const InputState* state, uint16 key) { - memcpy(input->state_old.keys_down, input->state.keys_down, MAX_KEY_PRESSES * sizeof(uint8)); - memcpy(input->state_old.keys_up, input->state.keys_up, MAX_KEY_PRESSES * sizeof(uint8)); + return (state->state_keys[0].key_id == key && state->state_keys[0].key_state < KEY_STATE_RELEASED) + || (state->state_keys[1].key_id == key && state->state_keys[1].key_state < KEY_STATE_RELEASED) + || (state->state_keys[2].key_id == key && state->state_keys[2].key_state < KEY_STATE_RELEASED) + || (state->state_keys[3].key_id == key && state->state_keys[3].key_state < KEY_STATE_RELEASED) + || (state->state_keys[4].key_id == key && state->state_keys[4].key_state < KEY_STATE_RELEASED) + || (state->state_keys[4].key_id == key && state->state_keys[4].key_state < KEY_STATE_RELEASED) + || (state->state_keys[5].key_id == key && state->state_keys[5].key_state < KEY_STATE_RELEASED) + || (state->state_keys[6].key_id == key && state->state_keys[6].key_state < KEY_STATE_RELEASED) + || (state->state_keys[7].key_id == key && state->state_keys[7].key_state < KEY_STATE_RELEASED) + || (state->state_keys[8].key_id == key && state->state_keys[8].key_state < KEY_STATE_RELEASED) + || (state->state_keys[9].key_id == key && state->state_keys[9].key_state < KEY_STATE_RELEASED); } inline -bool keyboard_is_pressed(const InputState* state, byte key) +bool input_is_pressed(const InputState* state, uint16 key) { - return state->keys_down[0] == key - || state->keys_down[1] == key - || state->keys_down[2] == key - || state->keys_down[3] == key - || state->keys_down[4] == key; + return (state->state_keys[0].key_id == key && state->state_keys[0].key_state == KEY_STATE_PRESSED) + || (state->state_keys[1].key_id == key && state->state_keys[1].key_state == KEY_STATE_PRESSED) + || (state->state_keys[2].key_id == key && state->state_keys[2].key_state == KEY_STATE_PRESSED) + || (state->state_keys[3].key_id == key && state->state_keys[3].key_state == KEY_STATE_PRESSED) + || (state->state_keys[4].key_id == key && state->state_keys[4].key_state == KEY_STATE_PRESSED) + || (state->state_keys[4].key_id == key && state->state_keys[4].key_state == KEY_STATE_PRESSED) + || (state->state_keys[5].key_id == key && state->state_keys[5].key_state == KEY_STATE_PRESSED) + || (state->state_keys[6].key_id == key && state->state_keys[6].key_state == KEY_STATE_PRESSED) + || (state->state_keys[7].key_id == key && state->state_keys[7].key_state == KEY_STATE_PRESSED) + || (state->state_keys[8].key_id == key && state->state_keys[8].key_state == KEY_STATE_PRESSED) + || (state->state_keys[9].key_id == key && state->state_keys[9].key_state == KEY_STATE_PRESSED); } inline -bool keyboard_is_released(const InputState* state, byte key) +bool input_is_held(const InputState* state, uint16 key) { - return state->keys_up[0] == key - || state->keys_up[1] == key - || state->keys_up[2] == key - || state->keys_up[3] == key - || state->keys_up[4] == key; + return (state->state_keys[0].key_id == key && state->state_keys[0].key_state == KEY_STATE_HELD) + || (state->state_keys[1].key_id == key && state->state_keys[1].key_state == KEY_STATE_HELD) + || (state->state_keys[2].key_id == key && state->state_keys[2].key_state == KEY_STATE_HELD) + || (state->state_keys[3].key_id == key && state->state_keys[3].key_state == KEY_STATE_HELD) + || (state->state_keys[4].key_id == key && state->state_keys[4].key_state == KEY_STATE_HELD) + || (state->state_keys[4].key_id == key && state->state_keys[4].key_state == KEY_STATE_HELD) + || (state->state_keys[5].key_id == key && state->state_keys[5].key_state == KEY_STATE_HELD) + || (state->state_keys[6].key_id == key && state->state_keys[6].key_state == KEY_STATE_HELD) + || (state->state_keys[7].key_id == key && state->state_keys[7].key_state == KEY_STATE_HELD) + || (state->state_keys[8].key_id == key && state->state_keys[8].key_state == KEY_STATE_HELD) + || (state->state_keys[9].key_id == key && state->state_keys[9].key_state == KEY_STATE_HELD); } inline -bool keyboard_are_pressed( +bool input_is_released(const InputState* state, uint16 key) +{ + return (state->state_keys[0].key_id == key && state->state_keys[0].key_state == KEY_STATE_RELEASED) + || (state->state_keys[1].key_id == key && state->state_keys[1].key_state == KEY_STATE_RELEASED) + || (state->state_keys[2].key_id == key && state->state_keys[2].key_state == KEY_STATE_RELEASED) + || (state->state_keys[3].key_id == key && state->state_keys[3].key_state == KEY_STATE_RELEASED) + || (state->state_keys[4].key_id == key && state->state_keys[4].key_state == KEY_STATE_RELEASED) + || (state->state_keys[4].key_id == key && state->state_keys[4].key_state == KEY_STATE_RELEASED) + || (state->state_keys[5].key_id == key && state->state_keys[5].key_state == KEY_STATE_RELEASED) + || (state->state_keys[6].key_id == key && state->state_keys[6].key_state == KEY_STATE_RELEASED) + || (state->state_keys[7].key_id == key && state->state_keys[7].key_state == KEY_STATE_RELEASED) + || (state->state_keys[8].key_id == key && state->state_keys[8].key_state == KEY_STATE_RELEASED) + || (state->state_keys[9].key_id == key && state->state_keys[9].key_state == KEY_STATE_RELEASED); +} + +inline +bool input_was_down(const InputState* state, uint16 key) +{ + return (state->state_keys[0].key_id == key && state->state_keys[0].key_state == KEY_STATE_RELEASED) + || (state->state_keys[1].key_id == key && state->state_keys[1].key_state == KEY_STATE_RELEASED) + || (state->state_keys[2].key_id == key && state->state_keys[2].key_state == KEY_STATE_RELEASED) + || (state->state_keys[3].key_id == key && state->state_keys[3].key_state == KEY_STATE_RELEASED) + || (state->state_keys[4].key_id == key && state->state_keys[4].key_state == KEY_STATE_RELEASED) + || (state->state_keys[4].key_id == key && state->state_keys[4].key_state == KEY_STATE_RELEASED) + || (state->state_keys[5].key_id == key && state->state_keys[5].key_state == KEY_STATE_RELEASED) + || (state->state_keys[6].key_id == key && state->state_keys[6].key_state == KEY_STATE_RELEASED) + || (state->state_keys[7].key_id == key && state->state_keys[7].key_state == KEY_STATE_RELEASED) + || (state->state_keys[8].key_id == key && state->state_keys[8].key_state == KEY_STATE_RELEASED) + || (state->state_keys[9].key_id == key && state->state_keys[9].key_state == KEY_STATE_RELEASED); +} + +inline +bool inputs_are_down( const InputState* state, - byte key0, byte key1 = 0, byte key2 = 0, byte key3 = 0, byte key4 = 0 + uint16 key0, uint16 key1 = 0, uint16 key2 = 0, uint16 key3 = 0, uint16 key4 = 0 ) { - return (key0 != 0 && keyboard_is_pressed(state, key0)) - && (key1 == 0 || keyboard_is_pressed(state, key1)) - && (key2 == 0 || keyboard_is_pressed(state, key2)) - && (key3 == 0 || keyboard_is_pressed(state, key3)) - && (key4 == 0 || keyboard_is_pressed(state, key4)); + return (key0 != 0 && input_is_down(state, key0)) + && (key1 == 0 || input_is_down(state, key1)) + && (key2 == 0 || input_is_down(state, key2)) + && (key3 == 0 || input_is_down(state, key3)) + && (key4 == 0 || input_is_down(state, key4)); } // We are binding hotkeys bi-directional @@ -209,32 +238,37 @@ input_add_hotkey( { int count = 0; - int key0_offset = ((bool) (key0 & INPUT_KEYBOARD_PREFIX)) * MAX_MOUSE_KEYS; - int key1_offset = ((bool) (key1 & INPUT_KEYBOARD_PREFIX)) * MAX_MOUSE_KEYS; - int key2_offset = ((bool) (key2 & INPUT_KEYBOARD_PREFIX)) * MAX_MOUSE_KEYS; - - key0 = key0 & ~INPUT_KEYBOARD_PREFIX; - key1 = key1 & ~INPUT_KEYBOARD_PREFIX; - key2 = key2 & ~INPUT_KEYBOARD_PREFIX; - // Define required keys for hotkey if (key0 != 0) { // Note: -1 since the hotkeys MUST start at 1 (0 is a special value for empty) - mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION] = (uint8) (key0 + key0_offset); + mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION] = (uint16) key0; ++count; } if (key1 != 0) { // Note: -1 since the hotkeys MUST start at 1 (0 is a special value for empty) - mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION + count] = (uint8) (key1 + key1_offset); + mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION + count] = (uint16) key1; ++count; } if (key2 != 0) { // Note: -1 since the hotkeys MUST start at 1 (0 is a special value for empty) - mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION + count] = (uint8) (key2 + key2_offset); + mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION + count] = (uint16) key2; } + int key0_offset = ((bool) (key0 & INPUT_KEYBOARD_PREFIX)) * MAX_MOUSE_KEYS + + ((bool) (key0 & INPUT_CONTROLLER_PREFIX)) * (MAX_MOUSE_KEYS + MAX_KEYBOARD_KEYS); + + int key1_offset = ((bool) (key1 & INPUT_KEYBOARD_PREFIX)) * MAX_MOUSE_KEYS + + ((bool) (key1 & INPUT_CONTROLLER_PREFIX)) * (MAX_MOUSE_KEYS + MAX_KEYBOARD_KEYS); + + int key2_offset = ((bool) (key2 & INPUT_KEYBOARD_PREFIX)) * MAX_MOUSE_KEYS + + ((bool) (key2 & INPUT_CONTROLLER_PREFIX)) * (MAX_MOUSE_KEYS + MAX_KEYBOARD_KEYS); + + key0 = (key0 & ~(INPUT_KEYBOARD_PREFIX | INPUT_CONTROLLER_PREFIX)); + key1 = (key1 & ~(INPUT_KEYBOARD_PREFIX | INPUT_CONTROLLER_PREFIX)); + key2 = (key2 & ~(INPUT_KEYBOARD_PREFIX | INPUT_CONTROLLER_PREFIX)); + // Bind key to hotkey for (int i = 0; i < MAX_KEY_TO_HOTKEY; ++i) { if (key0 == 0 && key1 == 0 && key2 == 0) { @@ -270,64 +304,67 @@ bool hotkey_is_active(const InputState* state, uint8 hotkey) // similar to hotkey_is_active but instead of just performing a lookup in the input_hotkey_state created results // this is actively checking the current input state (not the hotkey state) -// @performance This seems like a much better simpler solution no? -// However, it is probably a slower solution after calling this function many times? -// Remember, we would call this function for almost every possible hotkey (depending on context) per frame inline -bool hotkey_is_pressed(const InputState* __restrict state, const InputMapping* __restrict mapping, uint8 hotkey) +bool hotkey_keys_are_active(const InputState* __restrict state, const InputMapping* __restrict mapping, uint8 hotkey) { - uint8 key0 = mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION]; - uint8 key1 = mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION + 1]; - uint8 key2 = mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION + 2]; + uint16 key0 = mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION]; + uint16 key1 = mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION + 1]; + uint16 key2 = mapping->hotkeys[(hotkey - 1) * MAX_HOTKEY_COMBINATION + 2]; - bool is_pressed = false; - if (key0 > MAX_MOUSE_KEYS) { - key0 -= MAX_MOUSE_KEYS; - is_pressed = keyboard_is_pressed(state, key0); - } else if (key0 > 0) { - is_pressed = IS_BIT_SET_R2L(state->mouse_down, key0 - 1); + // This may seem a little bit confusing but we don't care if a input key is down or up + // Any state means it was used recently BUT NOT YET HANDLED + // If it was handled it would've been removed (at least in case of RELEASED) + // Therefore, if a key has a state -> treat it as if active + bool is_active = input_action_exists(state, key0); + if (!is_active || key1 == 0) { + return is_active; } - if (!is_pressed || key1 == 0) { - return is_pressed; + is_active &= input_action_exists(state, key1); + if (!is_active || key2 == 0) { + return is_active; } - if (key1 > MAX_MOUSE_KEYS) { - key1 -= MAX_MOUSE_KEYS; - is_pressed &= keyboard_is_pressed(state, key1); - } else if (key1 > 0) { - is_pressed &= IS_BIT_SET_R2L(state->mouse_down, key1 - 1); + return (is_active &= input_action_exists(state, key2)); +} + +inline +void input_set_state(InputState* state, uint16 key_id, uint16 new_state) +{ + InputKey* free_state = NULL; + bool action_required = true; + + for (int j = 0; j < MAX_KEY_STATES; ++j) { + if (!free_state && state->state_keys[j].key_id == 0) { + free_state = &state->state_keys[j]; + } else if (state->state_keys[j].key_id == key_id) { + state->state_keys[j].key_state = new_state; + action_required = false; + } } - if (!is_pressed || key2 == 0) { - return is_pressed; + if (!action_required || !free_state) { + return; } - if (key2 > MAX_MOUSE_KEYS) { - key2 -= MAX_MOUSE_KEYS; - is_pressed &= keyboard_is_pressed(state, key2); - } else if (key2 > 0) { - is_pressed &= IS_BIT_SET_R2L(state->mouse_down, key2 - 1); - } - - return is_pressed; + free_state->key_id = key_id; + free_state->key_state = new_state; + // @todo implement + // free_state->time = 0; } void input_hotkey_state(InputState* __restrict state, const InputMapping* mapping) { - // @bug isn't there a bug, MAX_KEY_PRESSES is the keyboard limit, what about additional mouse inputs? - memset(state->state_hotkeys, 0, sizeof(uint8) * MAX_KEY_PRESSES); int i = 0; - // @performance It would be nice if we could skip this loop by checking keyboard_changed similar to the mouse loop further down - // The problem is that this loop checks both mouse and keyboard - // Check every key down state - for (int down_state = 0; down_state < MAX_KEY_PRESSES; ++down_state) { - if (state->keys_down[down_state] == 0) { + for (int key_state = 0; key_state < MAX_KEY_STATES; ++key_state) { + if (state->state_keys[key_state].key_id == 0 + || state->state_keys[key_state].key_state == KEY_STATE_RELEASED + ) { // no key defined for this down state continue; } @@ -335,7 +372,12 @@ input_hotkey_state(InputState* __restrict state, const InputMapping* mapping) // Is a key defined for this state AND is at least one hotkey defined for this key // If no hotkey is defined we don't care // Careful, remember MAX_MOUSE_KEYS offset - const uint8* hotkeys_for_key = mapping->keys[state->keys_down[down_state] + MAX_MOUSE_KEYS - 1]; + InputKey* input = &state->state_keys[key_state]; + int32 internal_key_id = (input->key_id & ~(INPUT_KEYBOARD_PREFIX | INPUT_CONTROLLER_PREFIX)) + + ((bool) (input->key_id & INPUT_KEYBOARD_PREFIX)) * MAX_MOUSE_KEYS + + ((bool) (input->key_id & INPUT_CONTROLLER_PREFIX)) * (MAX_MOUSE_KEYS + MAX_KEYBOARD_KEYS); + + const uint8* hotkeys_for_key = mapping->keys[internal_key_id - 1]; if (hotkeys_for_key[0] == 0) { // no possible hotkey associated with this key continue; @@ -349,42 +391,7 @@ input_hotkey_state(InputState* __restrict state, const InputMapping* mapping) return; } - bool is_pressed = hotkey_is_pressed(state, mapping, hotkeys_for_key[possible_hotkey_idx]); - - // store active hotkey, if it is not already active - if (is_pressed && !hotkey_is_active(state, hotkeys_for_key[possible_hotkey_idx])) { - state->state_hotkeys[i] = hotkeys_for_key[possible_hotkey_idx]; - ++i; - } - } - } - - // @performance we could also check if the mouse state even changed - if (state->mouse_down == 0 || i >= MAX_KEY_PRESSES) { - return; - } - - // We now also need to check if there are hotkeys for the mouse buttons - // Some are already handled in the previous section, but some might not be handled, since they are mouse only - // But this also means, that we ONLY have to search for mouse only hotkeys. It's impossible to find NEW matches with keyboard keys. - for (int down_state = 0; down_state < MAX_MOUSE_KEYS; ++down_state) { - if (!IS_BIT_SET_R2L(state->mouse_down, down_state)) { - continue; - } - - const uint8* hotkeys_for_key = mapping->keys[down_state]; - if (hotkeys_for_key[0] == 0) { - // no possible hotkey associated with this key - continue; - } - - for (int possible_hotkey_idx = 0; possible_hotkey_idx < MAX_KEY_TO_HOTKEY; ++possible_hotkey_idx) { - // We only support a slimited amount of active hotkeys - if (i >= MAX_KEY_PRESSES) { - return; - } - - bool is_pressed = hotkey_is_pressed(state, mapping, hotkeys_for_key[possible_hotkey_idx]); + bool is_pressed = hotkey_keys_are_active(state, mapping, hotkeys_for_key[possible_hotkey_idx]); // store active hotkey, if it is not already active if (is_pressed && !hotkey_is_active(state, hotkeys_for_key[possible_hotkey_idx])) { diff --git a/platform/win32/input/RawInput.h b/platform/win32/input/RawInput.h index 693b68e..2c956b0 100644 --- a/platform/win32/input/RawInput.h +++ b/platform/win32/input/RawInput.h @@ -17,6 +17,7 @@ #include "../../../utils/MathUtils.h" #include "../../../memory/RingMemory.h" #include "../../../memory/BufferMemory.h" +#include "../../../stdlib/simd/SIMD_I8.h" #include #define INPUT_MOUSE_BUTTON_1 1 @@ -46,6 +47,7 @@ int input_init(HWND hwnd, Input* __restrict states, RingMemory* ring) int32 mouse_found = 0; int32 keyboard_found = 0; + int32 controller_found = 0; int32 i; for (i = 0; i < device_count; ++i) { @@ -53,29 +55,85 @@ int input_init(HWND hwnd, Input* __restrict states, RingMemory* ring) RID_DEVICE_INFO rdi; GetRawInputDeviceInfoA(pRawInputDeviceList[i].hDevice, RIDI_DEVICEINFO, &rdi, &cb_size); + RAWINPUTDEVICE rid[1]; + switch (rdi.dwType) { case RIM_TYPEMOUSE: { - // @bug Would need fixing once we support controllers here if (states[mouse_found].handle_mouse != NULL) { ++mouse_found; } states[mouse_found].handle_mouse = pRawInputDeviceList[i].hDevice; states[mouse_found].is_connected = true; - states[mouse_found].type = INPUT_TYPE_MOUSE_KEYBOARD; + + // Mouse + rid[0].usUsagePage = 0x01; // @todo doesn't work with 0x05 for games? + rid[0].usUsage = 0x02; + rid[0].dwFlags = RIDEV_DEVNOTIFY; + rid[0].hwndTarget = hwnd; + + if (!RegisterRawInputDevices((PCRAWINPUTDEVICE) rid, 1, sizeof(RAWINPUTDEVICE))) { + // @todo Log + ASSERT_SIMPLE(false); + } } break; case RIM_TYPEKEYBOARD: { - // @bug Would need fixing once we support controllers here (keyboard + controller in one input bug) if (states[keyboard_found].handle_keyboard != NULL) { ++keyboard_found; } states[keyboard_found].handle_keyboard = pRawInputDeviceList[i].hDevice; states[keyboard_found].is_connected = true; - states[keyboard_found].type = INPUT_TYPE_MOUSE_KEYBOARD; + + // Keyboard + rid[0].usUsagePage = 0x01; // @todo doesn't work with 0x05 for games? + rid[0].usUsage = 0x06; + rid[0].dwFlags = RIDEV_DEVNOTIFY; + rid[0].hwndTarget = hwnd; + + if (!RegisterRawInputDevices((PCRAWINPUTDEVICE) rid, 1, sizeof(RAWINPUTDEVICE))) { + // @todo Log + ASSERT_SIMPLE(false); + } } break; case RIM_TYPEHID: { - states[i].type = INPUT_TYPE_OTHER; + if (rdi.hid.usUsage == 0x05) { + if (states[controller_found].handle_controller != NULL) { + ++controller_found; + } + + states[controller_found].handle_controller = pRawInputDeviceList[i].hDevice; + states[controller_found].is_connected = true; + + // Gamepad + rid[0].usUsagePage = 0x01; + rid[0].usUsage = 0x05; + rid[0].dwFlags = RIDEV_DEVNOTIFY; + rid[0].hwndTarget = hwnd; + + if (!RegisterRawInputDevices((PCRAWINPUTDEVICE) rid, 1, sizeof(RAWINPUTDEVICE))) { + // @todo Log + ASSERT_SIMPLE(false); + } + } else if (rdi.hid.usUsage == 0x04) { + if (states[controller_found].handle_controller != NULL) { + ++controller_found; + } + + states[controller_found].handle_controller = pRawInputDeviceList[i].hDevice; + states[controller_found].is_connected = true; + + // Joystick + rid[0].usUsagePage = 0x01; + rid[0].usUsage = 0x04; + rid[0].dwFlags = RIDEV_DEVNOTIFY; + rid[0].hwndTarget = hwnd; + + if (!RegisterRawInputDevices((PCRAWINPUTDEVICE) rid, 1, sizeof(RAWINPUTDEVICE))) { + // @todo Log + ASSERT_SIMPLE(false); + } + } } break; default: { @@ -83,37 +141,6 @@ int input_init(HWND hwnd, Input* __restrict states, RingMemory* ring) } } - RAWINPUTDEVICE rid[4]; - - // Mouse - rid[0].usUsagePage = 0x01; // @todo doesn't work with 0x05 for games? - rid[0].usUsage = 0x02; - rid[0].dwFlags = RIDEV_DEVNOTIFY; - rid[0].hwndTarget = hwnd; - - // Joystick - rid[1].usUsagePage = 0x01; // @todo doesn't work with 0x05 for games? - rid[1].usUsage = 0x04; - rid[1].dwFlags = RIDEV_DEVNOTIFY; - rid[1].hwndTarget = hwnd; - - // Gamepad - rid[2].usUsagePage = 0x01; // @todo doesn't work with 0x05 for games? - rid[2].usUsage = 0x05; - rid[2].dwFlags = RIDEV_DEVNOTIFY; - rid[2].hwndTarget = hwnd; - - // Keyboard - rid[3].usUsagePage = 0x01; // @todo doesn't work with 0x05 for games? - rid[3].usUsage = 0x06; - rid[3].dwFlags = RIDEV_DEVNOTIFY; - rid[3].hwndTarget = hwnd; - - if (!RegisterRawInputDevices((PCRAWINPUTDEVICE) rid, 4, sizeof(RAWINPUTDEVICE))) { - // @todo Log - ASSERT_SIMPLE(false); - } - return i; } @@ -130,7 +157,7 @@ void input_raw_handle(RAWINPUT* __restrict raw, Input* states, int state_count, { uint32 i = 0; if (raw->header.dwType == RIM_TYPEMOUSE) { - // @todo Change so we can directly access the correct state (maybe map handle address to index?) + // @performance Change so we can directly access the correct state (maybe map handle address to index?) while (i < state_count && states[i].handle_mouse != raw->header.hDevice ) { @@ -142,42 +169,44 @@ void input_raw_handle(RAWINPUT* __restrict raw, Input* states, int state_count, } if (raw->data.mouse.usButtonFlags) { - // @question should all of these be else ifs? + uint16 new_state; + uint16 button; + if (raw->data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN) { - states[i].state.mouse_down |= INPUT_MOUSE_BUTTON_1; - states[i].state.keys_down_time[0] = time; + new_state = KEY_STATE_PRESSED; + button = 1; } else if (raw->data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP) { - states[i].state.mouse_down &= ~INPUT_MOUSE_BUTTON_1; - } - - if (raw->data.mouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN) { - states[i].state.mouse_down |= INPUT_MOUSE_BUTTON_2; - states[i].state.keys_down_time[1] = time; + new_state = KEY_STATE_RELEASED; + button = 1; + } else if (raw->data.mouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN) { + new_state = KEY_STATE_PRESSED; + button = 2; } else if (raw->data.mouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP) { - states[i].state.mouse_down &= ~INPUT_MOUSE_BUTTON_2; - } - - if (raw->data.mouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN) { - states[i].state.mouse_down |= INPUT_MOUSE_BUTTON_3; - states[i].state.keys_down_time[2] = time; + new_state = KEY_STATE_RELEASED; + button = 2; + } else if (raw->data.mouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN) { + new_state = KEY_STATE_PRESSED; + button = 3; } else if (raw->data.mouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_UP) { - states[i].state.mouse_down &= ~INPUT_MOUSE_BUTTON_3; - } - - if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) { - states[i].state.mouse_down |= INPUT_MOUSE_BUTTON_4; - states[i].state.keys_down_time[3] = time; + new_state = KEY_STATE_RELEASED; + button = 3; + } else if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) { + new_state = KEY_STATE_PRESSED; + button = 4; } else if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP) { - states[i].state.mouse_down &= ~INPUT_MOUSE_BUTTON_4; - } - - if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) { - states[i].state.mouse_down |= INPUT_MOUSE_BUTTON_5; - states[i].state.keys_down_time[4] = time; + new_state = KEY_STATE_RELEASED; + button = 4; + } else if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) { + new_state = KEY_STATE_PRESSED; + button = 5; } else if (raw->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP) { - states[i].state.mouse_down &= ~INPUT_MOUSE_BUTTON_5; + new_state = KEY_STATE_RELEASED; + button = 5; + } else { + return; } + /* @todo implement if (raw->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) { states[i].state.wheel_delta += raw->data.mouse.usButtonData; } @@ -185,15 +214,15 @@ void input_raw_handle(RAWINPUT* __restrict raw, Input* states, int state_count, if (raw->data.mouse.usButtonFlags & RI_MOUSE_HWHEEL) { states[i].state.hwheel_delta += raw->data.mouse.usButtonData; } - - states[i].state_change_mouse = true; - states[i].state_change_mouse_button = true; + */ // @question is mouse wheel really considered a button change? - states[i].state_change_button = true; - } - if (states[i].mouse_movement) { + button |= INPUT_MOUSE_PREFIX; + + input_set_state(&states[i].state, button, new_state); + states[i].state_change_button = true; + } else if (states[i].mouse_movement) { // do we want to handle mouse movement for every individual movement, or do we want to pull it if (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { RECT rect; @@ -241,65 +270,67 @@ void input_raw_handle(RAWINPUT* __restrict raw, Input* states, int state_count, return; } - // @todo change to MakeCode instead of VKey - // @performance Some of the things down here seem unneccessary. We shouldn't have to loop all elements! + int16 new_state = -1; if (raw->data.keyboard.Flags == RI_KEY_BREAK) { - // Key is already released - if (keyboard_is_released(&states[i].state, (uint8) raw->data.keyboard.VKey)) { - for (int j = 0; j < MAX_KEY_PRESSES; ++j) { - if (states[i].state.keys_down[j] == (uint8) raw->data.keyboard.VKey) { - states[i].state.keys_down[j] = 0; - - break; - } - } - - return; - } - - bool empty = true; - for (int j = 0; j < MAX_KEY_PRESSES; ++j) { - if (empty && states[i].state.keys_up[j] == 0) { - states[i].state.keys_up[j] = (uint8) raw->data.keyboard.VKey; - - empty = false; - } - - // remove pressed key - if (states[i].state.keys_down[j] == (uint8) raw->data.keyboard.VKey) { - states[i].state.keys_down[j] = 0; - } - } + new_state = KEY_STATE_RELEASED; } else if (raw->data.keyboard.Flags == RI_KEY_MAKE) { - // Key is already released - if (keyboard_is_pressed(&states[i].state, (uint8) raw->data.keyboard.VKey)) { - for (int j = 0; j < MAX_KEY_PRESSES; ++j) { - if (states[i].state.keys_up[j] == (uint8) raw->data.keyboard.VKey) { - states[i].state.keys_up[j] = 0; - - break; - } - } - - return; - } - - bool empty = true; - for (int j = 0; j < MAX_KEY_PRESSES; ++j) { - if (empty && states[i].state.keys_down[j] == 0) { - states[i].state.keys_down[j] = (uint8) raw->data.keyboard.VKey; - states[i].state.keys_down_time[MAX_MOUSE_PRESSES + j] = time; - empty = false; - } - - // remove released key - if (states[i].state.keys_up[j] == (uint8) raw->data.keyboard.VKey) { - states[i].state.keys_up[j] = 0; - } - } + new_state = KEY_STATE_PRESSED; } + if (new_state < 0) { + return; + } + + // @todo change to MakeCode instead of VKey + uint16 key = raw->data.keyboard.VKey | INPUT_KEYBOARD_PREFIX; + + input_set_state(&states[i].state, key, new_state); states[i].state_change_button = true; + } else if (raw->header.dwType == RIM_TYPEHID) { + if (raw->header.dwSize > sizeof(RAWINPUT)) { + // @todo Find a way to handle most common controllers + // DualShock 3 + // Dualshock 4 + // DualSense + // Xbox + return; + + /* + while (i < state_count + && states[i].handle_controller != raw->header.hDevice + ) { + ++i; + } + + if (i >= state_count || !states[i].is_connected) { + return; + } + + // @performance The code below is horrible, we need to probably make it controller dependant + // Sometimes a controller may have a time component or a gyro which results in the controller + // constantly sending input data + + // @todo implement actual step usage + bool is_same = simd_compare( + raw->data.hid.bRawData, + states[i].state.controller_state, + raw->header.dwSize - sizeof(RAWINPUT), + 8 + ); + + if (!is_same) { + memcpy(states[i].state.controller_state, raw->data.hid.bRawData, raw->header.dwSize - sizeof(RAWINPUT)); + + char buffer[100]; + int j = 0; + for (j = 0; j < raw->header.dwSize - sizeof(RAWINPUT); ++j) { + buffer[j] = raw->data.hid.bRawData[j]; + } + + DEBUG_OUTPUT(buffer); + } + */ + } } } diff --git a/platform/win32/input/XInput.h b/platform/win32/input/XInput.h index 65dee55..0743fe3 100644 --- a/platform/win32/input/XInput.h +++ b/platform/win32/input/XInput.h @@ -33,6 +33,35 @@ DWORD WINAPI XInputSetStateStub(DWORD, XINPUT_VIBRATION*) { global_persist x_input_set_state* XInputSetState_ = XInputSetStateStub; #define XInputSetState XInputSetState_ +struct ControllerInput { + uint32 id = 0; + bool is_connected = false; + + // After handling the state change the game loop should set this to false + bool state_change = false; + + // @question maybe make part of button + bool up = false; + bool down = false; + bool left = false; + bool right = false; + + byte trigger_old[4]; + byte trigger[4]; + + // these are bitfields + uint16 button_old; + uint16 button; + + int16 stickl_x = 0; + int16 stickl_y = 0; + bool stickl_press = false; + + int16 stickr_x = 0; + int16 stickr_y = 0; + bool stickr_press = false; +}; + void xinput_load() { HMODULE lib = LoadLibraryExA((LPCSTR) "xinput1_4.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); if(!lib) { diff --git a/stdlib/simd/SIMD_I8.h b/stdlib/simd/SIMD_I8.h index cf38071..a252fed 100644 --- a/stdlib/simd/SIMD_I8.h +++ b/stdlib/simd/SIMD_I8.h @@ -459,7 +459,7 @@ inline int8_32 operator<=(int8_32 a, int8_32 b) inline int8_64 operator<=(int8_64 a, int8_64 b) { int8_64 simd; - simd.s = _mm512_mask_blend_epi8(_mm512_knot(_mm512_cmpgt_epi8_mask(b.s, a.s)), b.s, a.s); + simd.s = _mm512_mask_blend_epi8(_mm512_cmple_epi8_mask(a.s, b.s), b.s, a.s); return simd; } @@ -737,23 +737,23 @@ inline int8_64 clamp(int8_64 min_value, int8_64 a, int8_64 max_value) return simd_min(simd_max(a, min_value), max_value); } -inline int8 which_true(int8_16 a) +inline int32 which_true(int8_16 a) { - int8 which_true = _mm_movemask_epi8(a.s); + int32 which_true = _mm_movemask_epi8(a.s); return which_true; } -inline int8 which_true(int8_32 a) +inline int32 which_true(int8_32 a) { - int8 which_true = _mm256_movemask_epi8(a.s); + int32 which_true = _mm256_movemask_epi8(a.s); return which_true; } -inline int8 which_true(int8_64 a) +inline int64 which_true(int8_64 a) { - int8 which_true = _mm512_movepi8_mask(a.s); + int64 which_true = _mm512_movepi8_mask(a.s); return which_true; } @@ -853,4 +853,104 @@ f32 simd_mult(const int8* a, f32 b, int size, int steps) } */ +bool simd_compare_64(const byte* a, const byte* b) +{ + __m256i chunk1_a = _mm256_loadu_si256((__m256i*) a); + __m256i chunk1_b = _mm256_loadu_si256((__m256i*) b); + + __m256i chunk2_a = _mm256_loadu_si256((__m256i*) (a + 32)); + __m256i chunk2_b = _mm256_loadu_si256((__m256i*) (b + 32)); + + __m256i result1 = _mm256_cmpeq_epi8(chunk1_a, chunk1_b); + __m256i result2 = _mm256_cmpeq_epi8(chunk2_a, chunk2_b); + + __m256i combined = _mm256_and_si256(result1, result2); + + return _mm256_testc_si256(combined, _mm256_set1_epi8(-1)) != 1; +} + +int simd_compare(const byte* a, const byte* b, uint32 size, uint32 steps = 8) { + int i = 0; + + if (steps == 16) { + if (size >= 128) { + __m512i a_16; + __m512i b_16; + __mmask64 result_mask; + + for (; i <= size - 64; i += 64) { // 64 bytes per iteration + a_16 = _mm512_loadu_si512((__m512i*) a); + b_16 = _mm512_loadu_si512((__m512i*) b); + + result_mask = _mm512_cmpeq_epi8_mask(a_16, b_16); + + if (result_mask != 0xFFFFFFFFFFFFFFFF) { + return false; + } + + a += 64; + b += 64; + } + } + + if (size - i >= 64) { + return simd_compare(a, b, size - i, 8); + } else if (size - i >= 32) { + return simd_compare(a, b, size - i, 4); + } + } else if (steps == 8) { + if (size >= 64) { + __m256i a_8; + __m256i b_8; + __m256i result_8; + + for (; i <= size - steps; i += steps) { + a_8 = _mm256_loadu_si256((__m256i*) a); + b_8 = _mm256_loadu_si256((__m256i*) b); + + result_8 = _mm256_cmpeq_epi8(a_8, b_8); + + if (_mm256_testc_si256(result_8, _mm256_set1_epi8(-1)) != 1) { + return false; + } + + a += steps; + b += steps; + } + } + + if (size - i >= 32) { + return simd_compare(a, b, size - i, 4); + } + } else if (steps == 4) { + if (size >= 16) { + __m128i a_4; + __m128i b_4; + __m128i result_4; + + for (; i <= size - steps; i += steps) { + a_4 = _mm_loadu_si128((__m128i*) a); + b_4 = _mm_loadu_si128((__m128i*) b); + + result_4 = _mm_cmpeq_epi8(a_4, b_4); + + if (_mm_movemask_epi8(result_4) != 0xFFFF) { + return false; + } + + a += steps; + b += steps; + } + } + } + + for (; i < size; ++i) { + if (*a++ != *b++) { + return false; + } + } + + return true; +} + #endif \ No newline at end of file