From 6fad5bef4aaf13d638cd39138392dd4ed1e3bb62 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Mon, 8 Sep 2025 13:30:27 -0500 Subject: [PATCH] Input bind complete. --- src/console/cmd/cmdbind.h | 13 +++-- src/input/CMakeLists.txt | 2 + src/input/input.c | 112 ++++++++++++++++++++++++++------------ src/input/input.h | 97 +++++++++++++-------------------- src/input/inputbind.c | 30 ++++++++++ src/input/inputbind.h | 41 ++++++++++++++ src/input/inputbutton.c | 69 +++++++++++++++++++++++ src/input/inputbutton.h | 79 +++++++++++++++++++++++++++ src/util/math.h | 30 +++++++++- 9 files changed, 374 insertions(+), 99 deletions(-) create mode 100644 src/input/inputbind.c create mode 100644 src/input/inputbind.h create mode 100644 src/input/inputbutton.c create mode 100644 src/input/inputbutton.h diff --git a/src/console/cmd/cmdbind.h b/src/console/cmd/cmdbind.h index 7b3fa84..1a97ad1 100644 --- a/src/console/cmd/cmdbind.h +++ b/src/console/cmd/cmdbind.h @@ -17,11 +17,12 @@ void cmdBind(const consolecmdexec_t *exec) { if(exec->argc == 1) { consolePrint("TODO: Show binds"); - // consolePrint("Current binds:"); - // inputbinddata_t *data = INPUT.binds; - // do { + return; + } - // } while(data < INPUT.binds + INPUT_BIND_COUNT); + inputbutton_t button = inputButtonGetByName(exec->argv[0]); + if(button.type == INPUT_BUTTON_TYPE_NONE) { + consolePrint("Unknown button \"%s\"", exec->argv[0]); return; } @@ -31,5 +32,7 @@ void cmdBind(const consolecmdexec_t *exec) { return; } - consolePrint("test"); + // By default I unbind all. + inputUnbindButton(button); + inputBind(bind, button); } \ No newline at end of file diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 8b7dc59..83e5566 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -7,6 +7,8 @@ target_sources(${DUSK_TARGET_NAME} PRIVATE input.c + inputbutton.c + inputbind.c ) if(DUSK_TARGET_SYSTEM STREQUAL "linux") diff --git a/src/input/input.c b/src/input/input.c index 1165f8f..cd547ee 100644 --- a/src/input/input.c +++ b/src/input/input.c @@ -9,30 +9,23 @@ #include "assert/assert.h" #include "util/memory.h" #include "util/string.h" - -char_t INPUT_BIND_NAMES[INPUT_BIND_COUNT][16] = { - [INPUT_BIND_UP] = "UP", - [INPUT_BIND_DOWN] = "DOWN", - [INPUT_BIND_LEFT] = "LEFT", - [INPUT_BIND_RIGHT] = "RIGHT", - [INPUT_BIND_ACCEPT] = "ACCEPT", - [INPUT_BIND_CANCEL] = "CANCEL", - [INPUT_BIND_CONSOLE] = "CONSOLE" -}; +#include "util/math.h" input_t INPUT; void inputInit(void) { memoryZero(&INPUT, sizeof(input_t)); - INPUT.binds[INPUT_BIND_UP].keyboard[0] = SDL_SCANCODE_W; - INPUT.binds[INPUT_BIND_UP].keyboard[1] = SDL_SCANCODE_UP; - INPUT.binds[INPUT_BIND_CONSOLE].keyboard[0] = SDL_SCANCODE_GRAVE; + for(uint8_t i = 0; i < INPUT_BIND_COUNT; i++) { + INPUT.binds[i].bind = (inputbind_t)i; + INPUT.binds[i].lastValue = 0.0f; + INPUT.binds[i].currentValue = 0.0f; + } } void inputUpdate(void) { #if INPUT_SDL2 == 1 - const uint8_t *keyboardState = SDL_GetKeyboardState(NULL); + INPUT.keyboardState = SDL_GetKeyboardState(NULL); #endif // For each input bind... @@ -40,21 +33,14 @@ void inputUpdate(void) { do { data->lastValue = data->currentValue; data->currentValue = 0.0f; - - // Handle keyboard - #if INPUT_KEYBOARD == 1 - for(uint32_t i = 0; i < INPUT_BIND_KEYBOARD_BUTTONS_MAX; i++) { - if(data->keyboard[i] == 0) break; - - #if INPUT_SDL2 == 1 - if(keyboardState[data->keyboard[i]]) { - data->currentValue = 1.0f; - break; - } - #endif - } - #endif - + + for(uint8_t i = 0; i < INPUT_BIND_BUTTONS_MAX; i++) { + inputbutton_t button = data->buttons[i]; + if(button.type == INPUT_BUTTON_TYPE_NONE) continue;// TODO: Break? + data->currentValue = mathMax( + inputButtonGetValue(button), data->currentValue + ); + } data++; } while(data < INPUT.binds + INPUT_BIND_COUNT); } @@ -85,12 +71,68 @@ bool_t inputReleased(const inputbind_t bind) { return !inputIsDown(bind) && inputWasDown(bind); } -inputbind_t inputBindGetByName(const char_t *name) { - assertNotNull(name, "name must not be NULL"); - for(inputbind_t i = 0; i < INPUT_BIND_COUNT; i++) { - if(stringCompareInsensitive(INPUT_BIND_NAMES[i], name) == 0) { - return i; +void inputBind(const inputbind_t bind, const inputbutton_t button) { + assertTrue(bind < INPUT_BIND_COUNT, "Input bind out of bounds"); + + inputbinddata_t *data = &INPUT.binds[bind]; + + for(uint8_t i = 0; i < INPUT_BIND_BUTTONS_MAX; i++) { + if(data->buttons[i].type != INPUT_BUTTON_TYPE_NONE) continue; + data->buttons[i] = button; + return; + } +} + +void inputUnbind(const inputbind_t bind, const inputbutton_t button) { + assertTrue(bind < INPUT_BIND_COUNT, "Input bind out of bounds"); + + inputbinddata_t *data = &INPUT.binds[bind]; + + for(uint8_t i = 0; i < INPUT_BIND_BUTTONS_MAX; i++) { + if(data->buttons[i].type == INPUT_BUTTON_TYPE_NONE) break; + if(memoryCompare(&data->buttons[i], &button, sizeof(inputbutton_t)) != 0) { + continue; + } + + // Shift remaining buttons down + memoryMove( + &data->buttons[i], + &data->buttons[i + 1], + sizeof(inputbutton_t) * (INPUT_BIND_BUTTONS_MAX - i - 1) + ); + data->buttons[INPUT_BIND_BUTTONS_MAX - 1].type = INPUT_BUTTON_TYPE_NONE; + return; + } +} + +void inputUnbindAll(void) { + for(uint8_t i = 0; i < INPUT_BIND_COUNT; i++) { + memoryZero( + &INPUT.binds[i].buttons, + sizeof(inputbutton_t) * INPUT_BIND_BUTTONS_MAX + ); + } +} + +void inputUnbindButton(const inputbutton_t button) { + for(uint8_t i = 0; i < INPUT_BIND_COUNT; i++) { + inputbinddata_t *data = &INPUT.binds[i]; + for(uint8_t j = 0; j < INPUT_BIND_BUTTONS_MAX; j++) { + if(data->buttons[j].type == INPUT_BUTTON_TYPE_NONE) break; + if(memoryCompare(&data->buttons[j], &button, sizeof(inputbutton_t)) != 0) { + continue; + } + + inputUnbind((inputbind_t)i, button); } } - return INPUT_BIND_COUNT; +} + +void inputUnbindBind(const inputbind_t bind) { + assertTrue(bind < INPUT_BIND_COUNT, "Input bind out of bounds"); + + memoryZero( + &INPUT.binds[bind].buttons, + sizeof(inputbutton_t) * INPUT_BIND_BUTTONS_MAX + ); } \ No newline at end of file diff --git a/src/input/input.h b/src/input/input.h index 25c3c7f..705f9ad 100644 --- a/src/input/input.h +++ b/src/input/input.h @@ -6,65 +6,19 @@ */ #pragma once -#include "dusk.h" - -#if INPUT_SDL2 == 1 - #include -#else - #error "No input backend defined" -#endif - -// Keyboard defs -#if INPUT_KEYBOARD == 1 - #define INPUT_BIND_KEYBOARD_BUTTONS_MAX 32 - - #if INPUT_SDL2 == 1 - typedef SDL_Scancode inputscancode_t; - #endif -#endif - -// Gamepad defs -#if INPUT_GAMEPAD == 1 - #define INPUT_GAMEPAD_BUTTON_NAME_MAX 8 - #define INPUT_BIND_GAMEPAD_BUTTONS_MAX 8 - - #if INPUT_SDL2 == 1 - typedef SDL_GameControllerButton inputgamepadbutton_t; - #endif -#endif - -typedef enum { - INPUT_BIND_UP, - INPUT_BIND_DOWN, - INPUT_BIND_LEFT, - INPUT_BIND_RIGHT, - INPUT_BIND_ACCEPT, - INPUT_BIND_CANCEL, - INPUT_BIND_CONSOLE, - - INPUT_BIND_COUNT -} inputbind_t; - -typedef struct { - inputbind_t bind; - float_t lastValue; - float_t currentValue; - - #if INPUT_GAMEPAD == 1 - inputgamepadbutton_t gamepad[INPUT_BIND_GAMEPAD_BUTTONS_MAX]; - uint8_t gamepadButtonCount; - #endif - - #if INPUT_KEYBOARD == 1 - inputscancode_t keyboard[INPUT_BIND_KEYBOARD_BUTTONS_MAX]; - #endif -} inputbinddata_t; +#include "inputbutton.h" +#include "inputbind.h" typedef struct { inputbinddata_t binds[INPUT_BIND_COUNT]; + + #if INPUT_SDL2 == 1 + #if INPUT_KEYBOARD == 1 + const uint8_t *keyboardState; + #endif + #endif } input_t; -extern char_t INPUT_BIND_NAMES[INPUT_BIND_COUNT][16]; extern input_t INPUT; /** @@ -127,9 +81,36 @@ bool_t inputPressed(const inputbind_t bind); bool_t inputReleased(const inputbind_t bind); /** - * Gets an input bind by its name. + * Binds an input button to a specific input bind. * - * @param name The name of the input bind. - * @return The input bind, or INPUT_BIND_COUNT if not found. + * @param bind The input bind to bind the button to. + * @param button The input button to bind. */ -inputbind_t inputBindGetByName(const char_t *name); \ No newline at end of file +void inputBind(const inputbind_t bind, const inputbutton_t button); + +/** + * Unbinds an input button from a specific input bind. + * + * @param bind The input bind to unbind the button from. + * @param button The input button to unbind. + */ +void inputUnbind(const inputbind_t bind, const inputbutton_t button); + +/** + * Unbinds all input buttons from all input binds. + */ +void inputUnbindAll(); + +/** + * Unbinds an input button from all input binds. + * + * @param button The input button to unbind. + */ +void inputUnbindButton(const inputbutton_t button); + +/** + * Unbinds all input buttons from a specific input bind. + * + * @param bind The input bind to unbind all buttons from. + */ +void inputUnbindBind(const inputbind_t bind); \ No newline at end of file diff --git a/src/input/inputbind.c b/src/input/inputbind.c new file mode 100644 index 0000000..2d8b870 --- /dev/null +++ b/src/input/inputbind.c @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "inputbind.h" +#include "assert/assert.h" +#include "util/string.h" + +const char_t INPUT_BIND_NAMES[INPUT_BIND_COUNT][INPUT_BIND_NAME_MAX] = { + [INPUT_BIND_UP] = "UP", + [INPUT_BIND_DOWN] = "DOWN", + [INPUT_BIND_LEFT] = "LEFT", + [INPUT_BIND_RIGHT] = "RIGHT", + [INPUT_BIND_ACCEPT] = "ACCEPT", + [INPUT_BIND_CANCEL] = "CANCEL", + [INPUT_BIND_CONSOLE] = "CONSOLE" +}; + +inputbind_t inputBindGetByName(const char_t *name) { + assertNotNull(name, "name must not be NULL"); + for(inputbind_t i = 0; i < INPUT_BIND_COUNT; i++) { + if(stringCompareInsensitive(INPUT_BIND_NAMES[i], name) == 0) { + return i; + } + } + return INPUT_BIND_COUNT; +} \ No newline at end of file diff --git a/src/input/inputbind.h b/src/input/inputbind.h new file mode 100644 index 0000000..515ab80 --- /dev/null +++ b/src/input/inputbind.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "inputbutton.h" + +#define INPUT_BIND_NAME_MAX 16 +#define INPUT_BIND_BUTTONS_MAX 32 + +typedef enum { + INPUT_BIND_UP, + INPUT_BIND_DOWN, + INPUT_BIND_LEFT, + INPUT_BIND_RIGHT, + INPUT_BIND_ACCEPT, + INPUT_BIND_CANCEL, + INPUT_BIND_CONSOLE, + + INPUT_BIND_COUNT +} inputbind_t; + +typedef struct { + inputbind_t bind; + float_t lastValue; + float_t currentValue; + inputbutton_t buttons[INPUT_BIND_BUTTONS_MAX]; +} inputbinddata_t; + +extern const char_t INPUT_BIND_NAMES[INPUT_BIND_COUNT][INPUT_BIND_NAME_MAX]; + +/** + * Gets an input bind by its name. + * + * @param name The name of the input bind. + * @return The input bind, or INPUT_BIND_COUNT if not found. + */ +inputbind_t inputBindGetByName(const char_t *name); \ No newline at end of file diff --git a/src/input/inputbutton.c b/src/input/inputbutton.c new file mode 100644 index 0000000..b6f97fb --- /dev/null +++ b/src/input/inputbutton.c @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "inputbutton.h" +#include "input.h" +#include "assert/assert.h" +#include "util/string.h" + +const inputbuttondata_t INPUT_BUTTON_DATA[] = { + #if INPUT_SDL2 == 1 + #if INPUT_GAMEPAD == 1 + #if PSP + #else + #endif + #endif + + #if INPUT_KEYBOARD == 1 + { .name = "a", { .type = INPUT_BUTTON_TYPE_KEYBOARD, .scancode = SDL_SCANCODE_A } }, + { .name = "`", { .type = INPUT_BUTTON_TYPE_KEYBOARD, .scancode = SDL_SCANCODE_GRAVE } }, + #endif + #endif +}; + +inputbutton_t inputButtonGetByName(const char_t *name) { + assertNotNull(name, "name must not be NULL"); + + uint32_t len = sizeof(INPUT_BUTTON_DATA) / sizeof(inputbuttondata_t); + + for(uint32_t i = 0; i < len; i++) { + if(stringCompareInsensitive(INPUT_BUTTON_DATA[i].name, name) != 0) continue; + return INPUT_BUTTON_DATA[i].button; + } + + return (inputbutton_t){ .type = INPUT_BUTTON_TYPE_NONE }; +} + +float_t inputButtonGetValue(const inputbutton_t button) { + switch(button.type) { + #if INPUT_KEYBOARD == 1 + case INPUT_BUTTON_TYPE_KEYBOARD: { + #if INPUT_SDL2 == 1 + return INPUT.keyboardState[button.scancode] ? 1.0f : 0.0f; + #else + return 0.0f; + #endif + } + #endif + + #if INPUT_GAMEPAD == 1 + case INPUT_BUTTON_TYPE_GAMEPAD: { + #if INPUT_SDL2 == 1 + if(SDL_GameControllerGetButton( + SDL_GameControllerFromInstanceID(0), button.gpButton + )) return 1.0f; + #endif + return 0.0f; + } + #endif + + default: { + assertUnreachable("Unknown input button type"); + return 0.0f; + } + } +} \ No newline at end of file diff --git a/src/input/inputbutton.h b/src/input/inputbutton.h new file mode 100644 index 0000000..6402096 --- /dev/null +++ b/src/input/inputbutton.h @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +#if INPUT_SDL2 == 1 + #include +#else + #error "No input backend defined" +#endif + +// Keyboard defs +#if INPUT_KEYBOARD == 1 + #if INPUT_SDL2 == 1 + typedef SDL_Scancode inputscancode_t; + #endif +#endif + +// Gamepad defs +#if INPUT_GAMEPAD == 1 + #if INPUT_SDL2 == 1 + typedef SDL_GameControllerButton inputgamepadbutton_t; + #endif +#endif + +typedef enum { + INPUT_BUTTON_TYPE_NONE, + + #if INPUT_KEYBOARD == 1 + INPUT_BUTTON_TYPE_KEYBOARD, + #endif + #if INPUT_GAMEPAD == 1 + INPUT_BUTTON_TYPE_GAMEPAD, + #endif + + INPUT_BUTTON_TYPE_COUNT +} inputbuttontype_t; + +typedef struct { + inputbuttontype_t type; + + union { + #if INPUT_GAMEPAD == 1 + inputgamepadbutton_t gpButton; + #endif + + #if INPUT_KEYBOARD == 1 + inputscancode_t scancode; + #endif + }; +} inputbutton_t; + +typedef struct { + const char_t *name; + inputbutton_t button; +} inputbuttondata_t; + +extern const inputbuttondata_t INPUT_BUTTON_DATA[]; + +/** + * Gets an input button by its name. + * + * @param name The name of the input button. + * @return The input button, or .type = INPUT_BUTTON_TYPE_NONE if not found. + */ +inputbutton_t inputButtonGetByName(const char_t *name); + +/** + * Gets the current value of an input button. + * + * @param button The input button. + * @return The current value of the input button (0.0f to 1.0f). + */ +float_t inputButtonGetValue(const inputbutton_t button); \ No newline at end of file diff --git a/src/util/math.h b/src/util/math.h index ac9bc07..5884113 100644 --- a/src/util/math.h +++ b/src/util/math.h @@ -14,4 +14,32 @@ * @param value The value to find the next power of two for. * @return The next power of two greater than or equal to the value. */ -uint32_t mathNextPowTwo(uint32_t value); \ No newline at end of file +uint32_t mathNextPowTwo(uint32_t value); + +/** + * Returns the maximum of two values. + * + * @param a The first value. + * @param b The second value. + * @return The maximum of the two values. + */ +#define mathMax(a, b) ((a) > (b) ? (a) : (b)) + +/** + * Returns the minimum of two values. + * + * @param a The first value. + * @param b The second value. + * @return The minimum of the two values. + */ +#define mathMin(a, b) ((a) < (b) ? (a) : (b)) + +/** + * Clamps a value between a lower and upper bound. + * + * @param x The value to clamp. + * @param lower The lower bound. + * @param upper The upper bound. + * @return The clamped value. + */ +#define mathClamp(x, lower, upper) (mathMin(upper, mathMax(lower, x))) \ No newline at end of file