2024-09-16 13:17:00 +00:00
|
|
|
#include "twn_input_c.h"
|
|
|
|
#include "twn_util.h"
|
2024-07-08 00:44:20 +00:00
|
|
|
|
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
#include <stb_ds.h>
|
|
|
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
|
|
|
static void update_action_pressed_state(struct input_state *input, struct action *action) {
|
|
|
|
for (size_t i = 0; i < SDL_arraysize(action->bindings); ++i) {
|
|
|
|
switch (action->bindings[i].source) {
|
|
|
|
case BUTTON_SOURCE_NOT_SET:
|
|
|
|
break;
|
|
|
|
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-07-12 18:10:25 +00:00
|
|
|
action->position.x = (float)input->mouse_window_position.x;
|
|
|
|
action->position.x = (float)input->mouse_window_position.x;
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void input_bind_code_to_action(struct input_state *input,
|
2024-07-12 18:10:25 +00:00
|
|
|
char *action_name,
|
|
|
|
enum button_source source,
|
|
|
|
union button_code code)
|
2024-07-08 00:44:20 +00:00
|
|
|
{
|
|
|
|
struct action_hash_item *action_item = shgetp_null(input->action_hash, action_name);
|
|
|
|
if (action_item == NULL) {
|
|
|
|
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
struct action *action = &action_item->value;
|
|
|
|
|
|
|
|
/* check every binding to make sure this code isn't already bound */
|
|
|
|
for (size_t i = 0; i < SDL_arraysize(action->bindings); ++i) {
|
|
|
|
struct button *binding = &action->bindings[i];
|
|
|
|
|
|
|
|
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_KEYBOARD_CHARACTER:
|
|
|
|
is_already_bound = binding->code.keycode == code.keycode;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_already_bound) {
|
|
|
|
log_warn("(%s) Code already bound to action \"%s\".", __func__, action_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we're at max bindings, forget the first element and shift the rest */
|
|
|
|
if (action->num_bindings == SDL_arraysize(action->bindings)) {
|
|
|
|
--action->num_bindings;
|
|
|
|
size_t shifted_size = (sizeof action->bindings) - (sizeof action->bindings[0]);
|
|
|
|
memmove(action->bindings, action->bindings + 1, shifted_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
action->bindings[action->num_bindings++] = (struct button) {
|
|
|
|
.source = source,
|
|
|
|
.code = code,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void input_unbind_code_from_action(struct input_state *input,
|
|
|
|
char *action_name,
|
|
|
|
enum button_source source,
|
|
|
|
union button_code code)
|
|
|
|
{
|
|
|
|
struct action_hash_item *action_item = shgetp_null(input->action_hash, action_name);
|
|
|
|
if (action_item == NULL) {
|
|
|
|
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
struct action *action = &action_item->value;
|
|
|
|
|
|
|
|
/* check every binding to make sure this code is bound */
|
|
|
|
size_t index = 0;
|
|
|
|
bool is_bound = false;
|
|
|
|
for (index = 0; index < SDL_arraysize(action->bindings); ++index) {
|
|
|
|
struct button *binding = &action->bindings[index];
|
|
|
|
|
|
|
|
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_KEYBOARD_CHARACTER:
|
|
|
|
is_bound = binding->code.keycode == code.keycode;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_bound)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_bound) {
|
|
|
|
log_warn("(%s) Code is not bound to action \"%s\".", __func__, action_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove the element to unbind and shift the rest so there isn't a gap */
|
|
|
|
if (action->num_bindings == SDL_arraysize(action->bindings)) {
|
|
|
|
size_t elements_after_index = action->num_bindings - index;
|
|
|
|
size_t shifted_size = elements_after_index * (sizeof action->bindings[0]);
|
|
|
|
memmove(action->bindings + index, action->bindings + index + 1, shifted_size);
|
|
|
|
--action->num_bindings;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-07-12 18:10:25 +00:00
|
|
|
void input_state_init(struct input_state *input) {
|
2024-07-08 00:44:20 +00:00
|
|
|
sh_new_strdup(input->action_hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void input_state_deinit(struct input_state *input) {
|
|
|
|
shfree(input->action_hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void input_state_update(struct input_state *input) {
|
|
|
|
input->keyboard_state = SDL_GetKeyboardState(NULL);
|
|
|
|
input->mouse_state = SDL_GetMouseState(&input->mouse_window_position.x,
|
|
|
|
&input->mouse_window_position.y);
|
|
|
|
|
2024-07-30 21:05:28 +00:00
|
|
|
SDL_GetRelativeMouseState(&input->mouse_relative_position.x,
|
|
|
|
&input->mouse_relative_position.y);
|
|
|
|
|
2024-07-08 00:44:20 +00:00
|
|
|
for (size_t i = 0; i < shlenu(input->action_hash); ++i) {
|
|
|
|
struct action *action = &input->action_hash[i].value;
|
|
|
|
update_action_pressed_state(input, action);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void input_bind_action_scancode(struct input_state *input,
|
|
|
|
char *action_name,
|
|
|
|
SDL_Scancode scancode)
|
|
|
|
{
|
|
|
|
input_bind_code_to_action(input,
|
|
|
|
action_name,
|
|
|
|
BUTTON_SOURCE_KEYBOARD_PHYSICAL,
|
|
|
|
(union button_code) { .scancode = scancode });
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void input_unbind_action_scancode(struct input_state *input,
|
|
|
|
char *action_name,
|
|
|
|
SDL_Scancode scancode)
|
|
|
|
{
|
|
|
|
input_unbind_code_from_action(input,
|
|
|
|
action_name,
|
|
|
|
BUTTON_SOURCE_KEYBOARD_PHYSICAL,
|
|
|
|
(union button_code) { .scancode = scancode });
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void input_bind_action_mouse(struct input_state *input,
|
|
|
|
char *action_name,
|
|
|
|
uint8_t mouse_button)
|
|
|
|
{
|
|
|
|
input_bind_code_to_action(input,
|
|
|
|
action_name,
|
|
|
|
BUTTON_SOURCE_MOUSE,
|
|
|
|
(union button_code) { .mouse_button = mouse_button});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void input_unbind_action_mouse(struct input_state *input,
|
|
|
|
char *action_name,
|
|
|
|
uint8_t mouse_button)
|
|
|
|
{
|
|
|
|
input_unbind_code_from_action(input,
|
|
|
|
action_name,
|
|
|
|
BUTTON_SOURCE_MOUSE,
|
|
|
|
(union button_code) { .mouse_button = mouse_button});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void input_add_action(struct input_state *input, char *action_name) {
|
|
|
|
if (shgeti(input->action_hash, action_name) >= 0) {
|
|
|
|
log_warn("(%s) Action \"%s\" is already registered.", __func__, action_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
shput(input->action_hash, action_name, (struct action) { 0 });
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void input_delete_action(struct input_state *input, char *action_name) {
|
|
|
|
if (shdel(input->action_hash, action_name) == 0)
|
|
|
|
log_warn("(%s) Action \"%s\" is not registered.", __func__, action_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool input_is_action_pressed(struct input_state *input, char *action_name) {
|
|
|
|
struct action_hash_item *action = shgetp_null(input->action_hash, action_name);
|
|
|
|
if (action == NULL) {
|
|
|
|
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return action->value.is_pressed;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool input_is_action_just_pressed(struct input_state *input, char *action_name) {
|
|
|
|
struct action_hash_item *action = shgetp_null(input->action_hash, action_name);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool input_is_action_just_released(struct input_state *input, char *action_name) {
|
|
|
|
struct action_hash_item *action = shgetp_null(input->action_hash, action_name);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
t_fvec2 input_get_action_position(struct input_state *input, char *action_name) {
|
|
|
|
struct action_hash_item *action = shgetp_null(input->action_hash, action_name);
|
|
|
|
if (action == NULL) {
|
|
|
|
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
|
|
|
return (t_fvec2) { 0 };
|
|
|
|
}
|
|
|
|
|
|
|
|
return action->value.position;
|
|
|
|
}
|
2024-07-30 21:05:28 +00:00
|
|
|
|
|
|
|
|
|
|
|
void input_set_mouse_captured(struct input_state *input, bool enabled) {
|
2024-08-27 10:42:40 +00:00
|
|
|
(void)input;
|
2024-07-30 21:05:28 +00:00
|
|
|
/* TODO: returns -1 if not supported, but like... do we care? */
|
|
|
|
SDL_SetRelativeMouseMode(enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool input_is_mouse_captured(struct input_state *input) {
|
2024-08-27 10:42:40 +00:00
|
|
|
(void)input;
|
2024-07-30 21:05:28 +00:00
|
|
|
return SDL_GetRelativeMouseMode();
|
|
|
|
}
|
2024-08-21 13:55:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
void input_reset_state(struct input_state *input) {
|
|
|
|
stbds_shfree(input->action_hash);
|
|
|
|
}
|