2024-09-16 13:17:00 +00:00
|
|
|
#include "twn_input_c.h"
|
|
|
|
#include "twn_util.h"
|
2024-10-22 07:39:40 +00:00
|
|
|
#include "twn_util_c.h"
|
2024-10-01 00:13:58 +00:00
|
|
|
#include "twn_engine_context_c.h"
|
2024-10-22 07:39:40 +00:00
|
|
|
#include "twn_input.h"
|
2024-07-08 00:44:20 +00:00
|
|
|
|
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
#include <stb_ds.h>
|
|
|
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
|
|
|
|
2025-02-04 04:32:25 +00:00
|
|
|
struct ScancodeHashItem { char *const key; SDL_Scancode value; };
|
|
|
|
static struct ScancodeHashItem *control_to_scancode;
|
|
|
|
|
|
|
|
struct MouseButtonHashItem { char *const key; uint8_t value; };
|
|
|
|
static struct MouseButtonHashItem *control_to_mouse_mask;
|
|
|
|
|
|
|
|
/* prepares translation maps for controls */
|
|
|
|
static void init_control_maps(void) {
|
|
|
|
if (control_to_scancode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* these correspond to SDL_events.h definition, restricted to what deemed useful */
|
|
|
|
shput(control_to_scancode, "A", 4);
|
|
|
|
shput(control_to_scancode, "B", 5);
|
|
|
|
shput(control_to_scancode, "C", 6);
|
|
|
|
shput(control_to_scancode, "D", 7);
|
|
|
|
shput(control_to_scancode, "E", 8);
|
|
|
|
shput(control_to_scancode, "F", 9);
|
|
|
|
shput(control_to_scancode, "G", 10);
|
|
|
|
shput(control_to_scancode, "H", 11);
|
|
|
|
shput(control_to_scancode, "I", 12);
|
|
|
|
shput(control_to_scancode, "J", 13);
|
|
|
|
shput(control_to_scancode, "K", 14);
|
|
|
|
shput(control_to_scancode, "L", 15);
|
|
|
|
shput(control_to_scancode, "M", 16);
|
|
|
|
shput(control_to_scancode, "N", 17);
|
|
|
|
shput(control_to_scancode, "O", 18);
|
|
|
|
shput(control_to_scancode, "P", 19);
|
|
|
|
shput(control_to_scancode, "Q", 20);
|
|
|
|
shput(control_to_scancode, "R", 21);
|
|
|
|
shput(control_to_scancode, "S", 22);
|
|
|
|
shput(control_to_scancode, "T", 23);
|
|
|
|
shput(control_to_scancode, "U", 24);
|
|
|
|
shput(control_to_scancode, "V", 25);
|
|
|
|
shput(control_to_scancode, "W", 26);
|
|
|
|
shput(control_to_scancode, "X", 27);
|
|
|
|
shput(control_to_scancode, "Y", 28);
|
|
|
|
shput(control_to_scancode, "Z", 29);
|
|
|
|
shput(control_to_scancode, "1", 30);
|
|
|
|
shput(control_to_scancode, "2", 31);
|
|
|
|
shput(control_to_scancode, "3", 32);
|
|
|
|
shput(control_to_scancode, "4", 33);
|
|
|
|
shput(control_to_scancode, "5", 34);
|
|
|
|
shput(control_to_scancode, "6", 35);
|
|
|
|
shput(control_to_scancode, "7", 36);
|
|
|
|
shput(control_to_scancode, "8", 37);
|
|
|
|
shput(control_to_scancode, "9", 38);
|
|
|
|
shput(control_to_scancode, "0", 39);
|
|
|
|
shput(control_to_scancode, "RETURN", 40);
|
|
|
|
shput(control_to_scancode, "ENTER", 40); /* an alias */
|
|
|
|
shput(control_to_scancode, "ESCAPE", 41);
|
|
|
|
shput(control_to_scancode, "BACKSPACE", 42);
|
|
|
|
shput(control_to_scancode, "TAB", 43);
|
|
|
|
shput(control_to_scancode, "SPACE", 44);
|
|
|
|
shput(control_to_scancode, "MINUS", 45);
|
|
|
|
shput(control_to_scancode, "EQUALS", 46);
|
|
|
|
shput(control_to_scancode, "LEFTBRACKET", 47);
|
|
|
|
shput(control_to_scancode, "RIGHTBRACKET", 48);
|
|
|
|
shput(control_to_scancode, "BACKSLASH", 49);
|
|
|
|
shput(control_to_scancode, "NONUSHASH", 50);
|
|
|
|
shput(control_to_scancode, "SEMICOLON", 51);
|
|
|
|
shput(control_to_scancode, "APOSTROPHE", 52);
|
|
|
|
shput(control_to_scancode, "GRAVE", 53);
|
|
|
|
shput(control_to_scancode, "COMMA", 54);
|
|
|
|
shput(control_to_scancode, "PERIOD", 55);
|
|
|
|
shput(control_to_scancode, "SLASH", 56);
|
|
|
|
shput(control_to_scancode, "CAPSLOCK", 57);
|
|
|
|
shput(control_to_scancode, "F1", 58);
|
|
|
|
shput(control_to_scancode, "F2", 59);
|
|
|
|
shput(control_to_scancode, "F3", 60);
|
|
|
|
shput(control_to_scancode, "F4", 61);
|
|
|
|
shput(control_to_scancode, "F5", 62);
|
|
|
|
shput(control_to_scancode, "F6", 63);
|
|
|
|
shput(control_to_scancode, "F7", 64);
|
|
|
|
shput(control_to_scancode, "F8", 65);
|
|
|
|
shput(control_to_scancode, "F9", 66);
|
|
|
|
shput(control_to_scancode, "F10", 67);
|
|
|
|
shput(control_to_scancode, "F11", 68);
|
|
|
|
shput(control_to_scancode, "F12", 69);
|
|
|
|
shput(control_to_scancode, "PRINTSCREEN", 70);
|
|
|
|
shput(control_to_scancode, "SCROLLLOCK", 71);
|
|
|
|
shput(control_to_scancode, "PAUSE", 72);
|
|
|
|
shput(control_to_scancode, "INSERT", 73);
|
|
|
|
shput(control_to_scancode, "HOME", 74);
|
|
|
|
shput(control_to_scancode, "PAGEUP", 75);
|
|
|
|
shput(control_to_scancode, "DELETE", 76);
|
|
|
|
shput(control_to_scancode, "END", 77);
|
|
|
|
shput(control_to_scancode, "PAGEDOWN", 78);
|
|
|
|
shput(control_to_scancode, "RIGHT", 79);
|
|
|
|
shput(control_to_scancode, "LEFT", 80);
|
|
|
|
shput(control_to_scancode, "DOWN", 81);
|
|
|
|
shput(control_to_scancode, "UP", 82);
|
|
|
|
shput(control_to_scancode, "KPDIVIDE", 84);
|
|
|
|
shput(control_to_scancode, "KPMULTIPLY", 85);
|
|
|
|
shput(control_to_scancode, "KPMINUS", 86);
|
|
|
|
shput(control_to_scancode, "KPPLUS", 87);
|
|
|
|
shput(control_to_scancode, "KPENTER", 88);
|
|
|
|
shput(control_to_scancode, "KP1", 89);
|
|
|
|
shput(control_to_scancode, "KP2", 90);
|
|
|
|
shput(control_to_scancode, "KP3", 91);
|
|
|
|
shput(control_to_scancode, "KP4", 92);
|
|
|
|
shput(control_to_scancode, "KP5", 93);
|
|
|
|
shput(control_to_scancode, "KP6", 94);
|
|
|
|
shput(control_to_scancode, "KP7", 95);
|
|
|
|
shput(control_to_scancode, "KP8", 96);
|
|
|
|
shput(control_to_scancode, "KP9", 97);
|
|
|
|
shput(control_to_scancode, "KP0", 98);
|
|
|
|
shput(control_to_scancode, "LCTRL", 224);
|
|
|
|
shput(control_to_scancode, "LSHIFT", 225);
|
|
|
|
shput(control_to_scancode, "LALT", 226);
|
|
|
|
shput(control_to_scancode, "RCTRL", 228);
|
|
|
|
shput(control_to_scancode, "RSHIFT", 229);
|
|
|
|
|
|
|
|
/* TODO: support for double clicks */
|
|
|
|
shput(control_to_mouse_mask, "LCLICK", SDL_BUTTON(SDL_BUTTON_LEFT));
|
|
|
|
shput(control_to_mouse_mask, "MCLICK", SDL_BUTTON(SDL_BUTTON_MIDDLE));
|
|
|
|
shput(control_to_mouse_mask, "RCLICK", SDL_BUTTON(SDL_BUTTON_RIGHT));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-09-23 17:43:16 +00:00
|
|
|
static void update_action_pressed_state(InputState *input, Action *action) {
|
2024-10-01 00:13:58 +00:00
|
|
|
for (size_t i = 0; i < (uint64_t)ctx.keybind_slots; ++i) {
|
2024-07-08 00:44:20 +00:00
|
|
|
switch (action->bindings[i].source) {
|
|
|
|
case BUTTON_SOURCE_NOT_SET:
|
|
|
|
break;
|
2024-10-13 18:32:31 +00:00
|
|
|
case BUTTON_SOURCE_GAMEPAD:
|
|
|
|
CRY("Action pressed state updated failed", "BUTTON_SOURCE_GAMEPAD isn't handled");
|
|
|
|
break;
|
2024-07-08 00:44:20 +00:00
|
|
|
case BUTTON_SOURCE_KEYBOARD_PHYSICAL:
|
|
|
|
/* not pressed */
|
|
|
|
if (input->keyboard_state[action->bindings[i].code.scancode] == 0) {
|
|
|
|
action->just_changed = action->is_pressed;
|
|
|
|
action->is_pressed = false;
|
|
|
|
}
|
|
|
|
/* pressed */
|
|
|
|
else {
|
|
|
|
action->just_changed = !action->is_pressed;
|
|
|
|
action->is_pressed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BUTTON_SOURCE_MOUSE:
|
|
|
|
/* not pressed */
|
|
|
|
if ((input->mouse_state & action->bindings[i].code.mouse_button) == 0) {
|
|
|
|
action->just_changed = action->is_pressed;
|
|
|
|
action->is_pressed = false;
|
|
|
|
}
|
|
|
|
/* pressed */
|
|
|
|
else {
|
|
|
|
action->just_changed = !action->is_pressed;
|
|
|
|
action->is_pressed = true;
|
2024-12-24 07:24:50 +00:00
|
|
|
action->position.x = ((float)input->mouse_window_position.x - ctx.viewport_rect.x) / ctx.viewport_scale;
|
|
|
|
action->position.y = ((float)input->mouse_window_position.y - ctx.viewport_rect.y) / ctx.viewport_scale;
|
2024-07-08 00:44:20 +00:00
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
/* TODO: */
|
2024-07-08 00:44:20 +00:00
|
|
|
/*
|
|
|
|
* SDL_RenderWindowToLogical will turn window mouse
|
|
|
|
* coords into a position inside the logical render
|
|
|
|
* area. this has to be done to get an accurate point
|
|
|
|
* that can actually be used in game logic
|
|
|
|
*/
|
2024-07-12 18:10:25 +00:00
|
|
|
// SDL_RenderWindowToLogical(input->renderer,
|
|
|
|
// input->mouse_window_position.x,
|
|
|
|
// input->mouse_window_position.y,
|
|
|
|
// &action->position.x,
|
|
|
|
// &action->position.y);
|
2024-07-08 00:44:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-22 17:32:17 +00:00
|
|
|
static ActionHashItem *input_add_action(char const *action_name) {
|
|
|
|
SDL_assert(action_name);
|
|
|
|
|
|
|
|
Action new_action = { 0 };
|
2025-02-02 00:15:05 +00:00
|
|
|
new_action.bindings = ccalloc(ctx.keybind_slots, sizeof *new_action.bindings);
|
2024-10-22 17:32:17 +00:00
|
|
|
shput(ctx.input.action_hash, action_name, new_action);
|
|
|
|
return shgetp(ctx.input.action_hash, action_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void input_delete_action(char const *action_name) {
|
|
|
|
SDL_assert(action_name);
|
|
|
|
|
|
|
|
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
|
|
|
SDL_assert(action);
|
|
|
|
|
|
|
|
SDL_free(action->value.bindings);
|
|
|
|
shdel(ctx.input.action_hash, action_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-09-23 17:43:16 +00:00
|
|
|
static void input_bind_code_to_action(InputState *input,
|
2024-10-08 07:12:30 +00:00
|
|
|
char const *action_name,
|
2024-09-23 17:43:16 +00:00
|
|
|
ButtonSource source,
|
|
|
|
union ButtonCode code)
|
2024-07-08 00:44:20 +00:00
|
|
|
{
|
2024-09-23 17:43:16 +00:00
|
|
|
ActionHashItem *action_item = shgetp_null(input->action_hash, action_name);
|
2024-10-22 17:32:17 +00:00
|
|
|
if (!action_item)
|
|
|
|
action_item = input_add_action(action_name);
|
|
|
|
|
2024-09-23 17:43:16 +00:00
|
|
|
Action *action = &action_item->value;
|
2024-07-08 00:44:20 +00:00
|
|
|
|
|
|
|
/* check every binding to make sure this code isn't already bound */
|
2024-10-22 17:32:17 +00:00
|
|
|
for (size_t i = 0; i < action->num_bindings; ++i) {
|
2024-09-23 17:43:16 +00:00
|
|
|
Button *binding = &action->bindings[i];
|
2024-07-08 00:44:20 +00:00
|
|
|
|
|
|
|
if (binding->source != source)
|
|
|
|
break;
|
|
|
|
|
|
|
|
bool is_already_bound = false;
|
|
|
|
switch (binding->source) {
|
|
|
|
case BUTTON_SOURCE_NOT_SET:
|
|
|
|
break;
|
|
|
|
case BUTTON_SOURCE_KEYBOARD_PHYSICAL:
|
|
|
|
is_already_bound = binding->code.scancode == code.scancode;
|
|
|
|
break;
|
|
|
|
case BUTTON_SOURCE_GAMEPAD:
|
|
|
|
is_already_bound = binding->code.gamepad_button == code.gamepad_button;
|
|
|
|
break;
|
|
|
|
case BUTTON_SOURCE_MOUSE:
|
|
|
|
is_already_bound = binding->code.mouse_button == code.mouse_button;
|
|
|
|
break;
|
2024-10-13 18:32:31 +00:00
|
|
|
default:
|
|
|
|
SDL_assert(false);
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (is_already_bound) {
|
2024-10-22 17:32:17 +00:00
|
|
|
/* keep it alive */
|
|
|
|
binding->in_use = true;
|
2024-07-08 00:44:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we're at max bindings, forget the first element and shift the rest */
|
2024-10-01 00:13:58 +00:00
|
|
|
if (action->num_bindings == (uint64_t)ctx.keybind_slots) {
|
2024-07-08 00:44:20 +00:00
|
|
|
--action->num_bindings;
|
2024-10-22 17:32:17 +00:00
|
|
|
size_t shifted_size = sizeof action->bindings[0] * (ctx.keybind_slots - 1);
|
2024-09-25 22:42:34 +00:00
|
|
|
SDL_memmove(action->bindings, action->bindings + 1, shifted_size);
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
2024-09-23 17:43:16 +00:00
|
|
|
action->bindings[action->num_bindings++] = (Button) {
|
2024-07-08 00:44:20 +00:00
|
|
|
.source = source,
|
|
|
|
.code = code,
|
2024-10-22 17:32:17 +00:00
|
|
|
.in_use = true,
|
2024-07-08 00:44:20 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-09-23 17:43:16 +00:00
|
|
|
static void input_unbind_code_from_action(InputState *input,
|
2024-10-08 07:12:30 +00:00
|
|
|
char const *action_name,
|
2024-09-23 17:43:16 +00:00
|
|
|
ButtonSource source,
|
|
|
|
union ButtonCode code)
|
2024-07-08 00:44:20 +00:00
|
|
|
{
|
2024-09-23 17:43:16 +00:00
|
|
|
ActionHashItem *action_item = shgetp_null(input->action_hash, action_name);
|
2024-10-22 17:32:17 +00:00
|
|
|
if (!action_item)
|
|
|
|
action_item = input_add_action(action_name);
|
|
|
|
|
2024-09-23 17:43:16 +00:00
|
|
|
Action *action = &action_item->value;
|
2024-07-08 00:44:20 +00:00
|
|
|
|
|
|
|
/* check every binding to make sure this code is bound */
|
|
|
|
size_t index = 0;
|
|
|
|
bool is_bound = false;
|
2024-10-22 17:32:17 +00:00
|
|
|
for (index = 0; index < action->num_bindings; ++index) {
|
2024-09-23 17:43:16 +00:00
|
|
|
Button *binding = &action->bindings[index];
|
2024-07-08 00:44:20 +00:00
|
|
|
|
|
|
|
if (binding->source != source)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (binding->source) {
|
|
|
|
case BUTTON_SOURCE_NOT_SET:
|
|
|
|
break;
|
|
|
|
case BUTTON_SOURCE_KEYBOARD_PHYSICAL:
|
|
|
|
is_bound = binding->code.scancode == code.scancode;
|
|
|
|
break;
|
|
|
|
case BUTTON_SOURCE_GAMEPAD:
|
|
|
|
is_bound = binding->code.gamepad_button == code.gamepad_button;
|
|
|
|
break;
|
|
|
|
case BUTTON_SOURCE_MOUSE:
|
|
|
|
is_bound = binding->code.mouse_button == code.mouse_button;
|
|
|
|
break;
|
2024-10-13 18:32:31 +00:00
|
|
|
default:
|
|
|
|
SDL_assert(false);
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
2024-09-20 15:39:37 +00:00
|
|
|
/* stop early, this won't change */
|
2024-07-08 00:44:20 +00:00
|
|
|
if (is_bound)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-10-22 17:32:17 +00:00
|
|
|
if (!is_bound)
|
2024-07-08 00:44:20 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* remove the element to unbind and shift the rest so there isn't a gap */
|
2024-09-20 15:39:37 +00:00
|
|
|
size_t elements_after_index = action->num_bindings - index;
|
|
|
|
size_t shifted_size = elements_after_index * sizeof action->bindings[0];
|
2024-09-25 22:42:34 +00:00
|
|
|
SDL_memmove(action->bindings + index, action->bindings + index + 1, shifted_size);
|
2024-09-20 15:39:37 +00:00
|
|
|
--action->num_bindings;
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-09-23 17:43:16 +00:00
|
|
|
void input_state_init(InputState *input) {
|
2024-07-08 00:44:20 +00:00
|
|
|
sh_new_strdup(input->action_hash);
|
2025-02-04 04:32:25 +00:00
|
|
|
init_control_maps();
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-09-23 17:43:16 +00:00
|
|
|
void input_state_deinit(InputState *input) {
|
2025-02-04 04:32:25 +00:00
|
|
|
shfree(control_to_mouse_mask);
|
|
|
|
shfree(control_to_scancode);
|
2024-10-01 10:34:58 +00:00
|
|
|
input_reset_state(input);
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-01-14 21:43:46 +00:00
|
|
|
void input_state_update_postframe(InputState *input) {
|
2025-01-25 23:45:50 +00:00
|
|
|
(void)input;
|
2024-10-22 17:32:17 +00:00
|
|
|
/* TODO: don't spam it if it happens */
|
2025-01-13 23:07:54 +00:00
|
|
|
if (SDL_SetRelativeMouseMode(ctx.game_copy.mouse_capture && ctx.window_mouse_resident) != 0)
|
2024-10-22 17:32:17 +00:00
|
|
|
log_warn("(%s) Mouse capture isn't supported.", __func__);
|
2025-01-14 21:43:46 +00:00
|
|
|
}
|
2024-10-22 17:32:17 +00:00
|
|
|
|
2025-01-14 21:43:46 +00:00
|
|
|
|
|
|
|
void input_state_update(InputState *input) {
|
2024-10-29 09:25:24 +00:00
|
|
|
int x, y;
|
|
|
|
|
2024-07-08 00:44:20 +00:00
|
|
|
input->keyboard_state = SDL_GetKeyboardState(NULL);
|
2024-10-29 09:25:24 +00:00
|
|
|
input->mouse_state = SDL_GetMouseState(&x, &y);
|
|
|
|
input->mouse_window_position = (Vec2){ (float)x, (float)y };
|
2024-07-08 00:44:20 +00:00
|
|
|
|
2024-10-29 09:25:24 +00:00
|
|
|
SDL_GetRelativeMouseState(&x, &y);
|
|
|
|
input->mouse_relative_position = (Vec2){ (float)x, (float)y };
|
2024-07-30 21:05:28 +00:00
|
|
|
|
2025-01-24 18:52:11 +00:00
|
|
|
ctx.game.mouse_position.x = ((float)input->mouse_window_position.x - ctx.viewport_rect.x) / ctx.viewport_scale;
|
|
|
|
ctx.game.mouse_position.y = ((float)input->mouse_window_position.y - ctx.viewport_rect.y) / ctx.viewport_scale;
|
2025-01-13 23:07:54 +00:00
|
|
|
|
|
|
|
if (ctx.window_mouse_resident)
|
|
|
|
ctx.game.mouse_movement = input->mouse_relative_position;
|
2025-01-13 23:56:55 +00:00
|
|
|
else
|
|
|
|
ctx.game.mouse_movement = (Vec2){0};
|
2024-10-08 07:12:30 +00:00
|
|
|
|
2024-07-08 00:44:20 +00:00
|
|
|
for (size_t i = 0; i < shlenu(input->action_hash); ++i) {
|
2024-09-23 17:43:16 +00:00
|
|
|
Action *action = &input->action_hash[i].value;
|
2024-10-22 17:32:17 +00:00
|
|
|
/* collect unused */
|
|
|
|
for (size_t u = 0; u < action->num_bindings; ++u) {
|
|
|
|
Button *button = &action->bindings[u];
|
|
|
|
if (!button->in_use)
|
|
|
|
input_unbind_code_from_action(input, input->action_hash[i].key, button->source, button->code);
|
|
|
|
else
|
|
|
|
button->in_use = false;
|
|
|
|
}
|
|
|
|
|
2024-07-08 00:44:20 +00:00
|
|
|
update_action_pressed_state(input, action);
|
|
|
|
}
|
2024-10-22 17:32:17 +00:00
|
|
|
|
|
|
|
size_t removed = 0;
|
|
|
|
for (size_t i = 0; i < shlenu(input->action_hash); ++i) {
|
|
|
|
if (input->action_hash[i - removed].value.num_bindings == 0)
|
|
|
|
input_delete_action(input->action_hash[i - removed].key);
|
|
|
|
}
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-02-04 04:32:25 +00:00
|
|
|
static Button *infer_control_desc(char const *control) {
|
|
|
|
Button *result = NULL;
|
|
|
|
char *copy = SDL_strdup(control);
|
|
|
|
|
|
|
|
char *saveptr = NULL;
|
|
|
|
char const *part = SDL_strtokr(copy, "+", &saveptr);
|
|
|
|
do {
|
|
|
|
struct ScancodeHashItem const *scancode = shgetp_null(control_to_scancode, part);
|
|
|
|
if (scancode) {
|
|
|
|
Button const button = {
|
|
|
|
.source = BUTTON_SOURCE_KEYBOARD_PHYSICAL,
|
|
|
|
.code.scancode = scancode->value,
|
|
|
|
};
|
|
|
|
arrpush(result, button);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct MouseButtonHashItem const *mouse_button = shgetp_null(control_to_mouse_mask, part);
|
|
|
|
if (mouse_button) {
|
|
|
|
Button const button = {
|
|
|
|
.source = BUTTON_SOURCE_MOUSE,
|
|
|
|
.code.mouse_button = mouse_button->value,
|
|
|
|
};
|
|
|
|
arrpush(result, button);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_warn("Unknown control part given (%s)", part);
|
|
|
|
} while ((part = SDL_strtokr(NULL, "+", &saveptr)));
|
|
|
|
|
|
|
|
SDL_free(copy);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-29 09:25:24 +00:00
|
|
|
void input_action(char const *action_name,
|
2025-02-04 04:32:25 +00:00
|
|
|
char const *control)
|
2024-07-08 00:44:20 +00:00
|
|
|
{
|
2024-10-08 07:12:30 +00:00
|
|
|
SDL_assert_always(action_name);
|
2024-07-08 00:44:20 +00:00
|
|
|
|
2025-02-04 04:32:25 +00:00
|
|
|
Button *combo = infer_control_desc(control);
|
|
|
|
if (!combo) {
|
|
|
|
log_warn("Invalid control (%s) for action bind", control);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: */
|
|
|
|
if (arrlenu(combo) > 1) {
|
|
|
|
log_warn("TODO: Control combinations are not yet supported.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (combo[0].source == BUTTON_SOURCE_KEYBOARD_PHYSICAL)
|
2024-10-08 07:12:30 +00:00
|
|
|
input_bind_code_to_action(&ctx.input,
|
2024-07-08 00:44:20 +00:00
|
|
|
action_name,
|
|
|
|
BUTTON_SOURCE_KEYBOARD_PHYSICAL,
|
2025-02-04 04:32:25 +00:00
|
|
|
(union ButtonCode) { .scancode = combo[0].code.scancode });
|
2024-07-08 00:44:20 +00:00
|
|
|
|
2025-02-04 04:32:25 +00:00
|
|
|
else if (combo[0].source == BUTTON_SOURCE_MOUSE)
|
2024-10-08 07:12:30 +00:00
|
|
|
input_bind_code_to_action(&ctx.input,
|
|
|
|
action_name,
|
|
|
|
BUTTON_SOURCE_MOUSE,
|
2025-02-04 04:32:25 +00:00
|
|
|
(union ButtonCode) { .mouse_button = combo[0].code.mouse_button });
|
|
|
|
else
|
|
|
|
log_warn("(%s) Unsupported control source value given: %i.", __func__, control);
|
|
|
|
|
|
|
|
arrfree(combo);
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-29 09:25:24 +00:00
|
|
|
bool input_action_pressed(char const *action_name) {
|
2024-10-08 07:12:30 +00:00
|
|
|
SDL_assert_always(action_name);
|
|
|
|
|
|
|
|
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
2024-07-08 00:44:20 +00:00
|
|
|
if (action == NULL) {
|
|
|
|
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return action->value.is_pressed;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-29 09:25:24 +00:00
|
|
|
bool input_action_just_pressed(char const *action_name) {
|
2024-10-08 07:12:30 +00:00
|
|
|
SDL_assert_always(action_name);
|
|
|
|
|
|
|
|
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
2024-07-08 00:44:20 +00:00
|
|
|
if (action == NULL) {
|
|
|
|
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return action->value.is_pressed && action->value.just_changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-29 09:25:24 +00:00
|
|
|
bool input_action_just_released(char const *action_name) {
|
2024-10-08 07:12:30 +00:00
|
|
|
SDL_assert_always(action_name);
|
|
|
|
|
|
|
|
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
2024-07-08 00:44:20 +00:00
|
|
|
if (action == NULL) {
|
|
|
|
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return !action->value.is_pressed && action->value.just_changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-29 09:25:24 +00:00
|
|
|
Vec2 input_action_position(char const *action_name) {
|
2024-10-08 07:12:30 +00:00
|
|
|
SDL_assert_always(action_name);
|
|
|
|
|
|
|
|
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
2024-07-08 00:44:20 +00:00
|
|
|
if (action == NULL) {
|
|
|
|
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
2024-09-23 17:43:16 +00:00
|
|
|
return (Vec2) { 0 };
|
2024-07-08 00:44:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return action->value.position;
|
|
|
|
}
|
2024-07-30 21:05:28 +00:00
|
|
|
|
|
|
|
|
2024-09-23 17:43:16 +00:00
|
|
|
void input_reset_state(InputState *input) {
|
2024-10-01 00:13:58 +00:00
|
|
|
for (size_t i = 0; i < shlenu(input->action_hash); ++i) {
|
|
|
|
Action *action = &input->action_hash[i].value;
|
|
|
|
SDL_free(action->bindings);
|
|
|
|
}
|
|
|
|
|
2024-08-21 13:55:34 +00:00
|
|
|
stbds_shfree(input->action_hash);
|
|
|
|
}
|