twn_input: singleton rework, twn_control.h and fixes
This commit is contained in:
@ -4,6 +4,7 @@
|
||||
#include "twn_context.h"
|
||||
#include "twn_textures_c.h"
|
||||
#include "twn_audio_c.h"
|
||||
#include "twn_input_c.h"
|
||||
#include "twn_engine_api.h"
|
||||
#include "rendering/twn_draw_c.h"
|
||||
|
||||
@ -18,6 +19,8 @@ typedef struct EngineContext {
|
||||
/* user code facing context */
|
||||
Context game;
|
||||
|
||||
InputState input;
|
||||
|
||||
/* the program's actual argc and argv */
|
||||
int argc;
|
||||
char **argv;
|
||||
@ -64,6 +67,7 @@ typedef struct EngineContext {
|
||||
bool was_successful;
|
||||
} EngineContext;
|
||||
|
||||
/* TODO: does it need to be marked with TWN_API? */
|
||||
TWN_API extern EngineContext ctx;
|
||||
|
||||
#endif
|
||||
|
128
src/twn_input.c
128
src/twn_input.c
@ -1,5 +1,6 @@
|
||||
#include "twn_input_c.h"
|
||||
#include "twn_util.h"
|
||||
#include "twn_control.h"
|
||||
#include "twn_engine_context_c.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
@ -38,7 +39,7 @@ static void update_action_pressed_state(InputState *input, Action *action) {
|
||||
action->just_changed = !action->is_pressed;
|
||||
action->is_pressed = true;
|
||||
action->position.x = (float)input->mouse_window_position.x;
|
||||
action->position.x = (float)input->mouse_window_position.x;
|
||||
action->position.y = (float)input->mouse_window_position.y;
|
||||
|
||||
/* TODO: */
|
||||
/*
|
||||
@ -63,7 +64,7 @@ static void update_action_pressed_state(InputState *input, Action *action) {
|
||||
|
||||
|
||||
static void input_bind_code_to_action(InputState *input,
|
||||
char *action_name,
|
||||
char const *action_name,
|
||||
ButtonSource source,
|
||||
union ButtonCode code)
|
||||
{
|
||||
@ -120,7 +121,7 @@ static void input_bind_code_to_action(InputState *input,
|
||||
|
||||
|
||||
static void input_unbind_code_from_action(InputState *input,
|
||||
char *action_name,
|
||||
char const *action_name,
|
||||
ButtonSource source,
|
||||
union ButtonCode code)
|
||||
{
|
||||
@ -193,6 +194,9 @@ void input_state_update(InputState *input) {
|
||||
SDL_GetRelativeMouseState(&input->mouse_relative_position.x,
|
||||
&input->mouse_relative_position.y);
|
||||
|
||||
ctx.game.mouse_window_position = input->mouse_window_position;
|
||||
ctx.game.mouse_relative_position = input->mouse_relative_position;
|
||||
|
||||
for (size_t i = 0; i < shlenu(input->action_hash); ++i) {
|
||||
Action *action = &input->action_hash[i].value;
|
||||
update_action_pressed_state(input, action);
|
||||
@ -200,76 +204,82 @@ void input_state_update(InputState *input) {
|
||||
}
|
||||
|
||||
|
||||
void input_bind_action_scancode(InputState *input,
|
||||
char *action_name,
|
||||
Scancode scancode)
|
||||
void input_bind_action_control(char const *action_name,
|
||||
Control control)
|
||||
{
|
||||
input_bind_code_to_action(input,
|
||||
action_name,
|
||||
BUTTON_SOURCE_KEYBOARD_PHYSICAL,
|
||||
(union ButtonCode) { .scancode = scancode });
|
||||
}
|
||||
SDL_assert_always(action_name);
|
||||
|
||||
|
||||
void input_unbind_action_scancode(InputState *input,
|
||||
char *action_name,
|
||||
Scancode scancode)
|
||||
{
|
||||
input_unbind_code_from_action(input,
|
||||
if (CONTROL_SCANCODE_START <= control && control < CONTROL_SCANCODE_LIMIT)
|
||||
input_bind_code_to_action(&ctx.input,
|
||||
action_name,
|
||||
BUTTON_SOURCE_KEYBOARD_PHYSICAL,
|
||||
(union ButtonCode) { .scancode = scancode });
|
||||
}
|
||||
(union ButtonCode) { .scancode = (SDL_Scancode)control });
|
||||
|
||||
|
||||
void input_bind_action_mouse(InputState *input,
|
||||
char *action_name,
|
||||
uint8_t mouse_button)
|
||||
{
|
||||
input_bind_code_to_action(input,
|
||||
action_name,
|
||||
BUTTON_SOURCE_MOUSE,
|
||||
(union ButtonCode) { .mouse_button = mouse_button});
|
||||
}
|
||||
|
||||
|
||||
void input_unbind_action_mouse(InputState *input,
|
||||
char *action_name,
|
||||
uint8_t mouse_button)
|
||||
{
|
||||
input_unbind_code_from_action(input,
|
||||
else if (CONTROL_MOUSECODE_START <= control && control < CONTROL_MOUSECODE_LIMIT) {
|
||||
uint8_t const mouse_button = (uint8_t)(control - CONTROL_MOUSECODE_START);
|
||||
input_bind_code_to_action(&ctx.input,
|
||||
action_name,
|
||||
BUTTON_SOURCE_MOUSE,
|
||||
(union ButtonCode) { .mouse_button = mouse_button});
|
||||
(union ButtonCode) { .mouse_button = SDL_BUTTON(mouse_button)});
|
||||
} else
|
||||
log_warn("(%s) Invalid control value given: %i.", __func__, control);
|
||||
}
|
||||
|
||||
|
||||
void input_add_action(InputState *input, char *action_name) {
|
||||
if (shgeti(input->action_hash, action_name) >= 0) {
|
||||
void input_unbind_action_control(char const *action_name,
|
||||
Control control)
|
||||
{
|
||||
SDL_assert_always(action_name);
|
||||
|
||||
if (CONTROL_SCANCODE_START <= control && control < CONTROL_SCANCODE_LIMIT)
|
||||
input_unbind_code_from_action(&ctx.input,
|
||||
action_name,
|
||||
BUTTON_SOURCE_KEYBOARD_PHYSICAL,
|
||||
(union ButtonCode) { .scancode = (SDL_Scancode)control });
|
||||
|
||||
else if (CONTROL_MOUSECODE_START <= control && control < CONTROL_MOUSECODE_LIMIT) {
|
||||
uint8_t const mouse_button = (uint8_t)(control - CONTROL_MOUSECODE_START);
|
||||
input_unbind_code_from_action(&ctx.input,
|
||||
action_name,
|
||||
BUTTON_SOURCE_MOUSE,
|
||||
(union ButtonCode) { .mouse_button = SDL_BUTTON(mouse_button)});
|
||||
} else
|
||||
log_warn("(%s) Invalid control value given: %i.", __func__, control);
|
||||
}
|
||||
|
||||
|
||||
void input_add_action(char const *action_name) {
|
||||
SDL_assert_always(action_name);
|
||||
|
||||
if (shgeti(ctx.input.action_hash, action_name) >= 0) {
|
||||
log_warn("(%s) Action \"%s\" is already registered.", __func__, action_name);
|
||||
return;
|
||||
}
|
||||
|
||||
Action new_action = { 0 };
|
||||
new_action.bindings = ccalloc(ctx.keybind_slots, sizeof *new_action.bindings);
|
||||
shput(input->action_hash, action_name, new_action);
|
||||
shput(ctx.input.action_hash, action_name, new_action);
|
||||
}
|
||||
|
||||
|
||||
void input_delete_action(InputState *input, char *action_name) {
|
||||
ActionHashItem *action = shgetp_null(input->action_hash, action_name);
|
||||
void input_delete_action(char const *action_name) {
|
||||
SDL_assert_always(action_name);
|
||||
|
||||
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
||||
if (action == NULL) {
|
||||
log_warn("(%s) Action \"%s\" is not registered.", __func__, action_name);
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_free(action->value.bindings);
|
||||
shdel(input->action_hash, action_name);
|
||||
shdel(ctx.input.action_hash, action_name);
|
||||
}
|
||||
|
||||
|
||||
bool input_is_action_pressed(InputState *input, char *action_name) {
|
||||
ActionHashItem *action = shgetp_null(input->action_hash, action_name);
|
||||
bool input_is_action_pressed(char const *action_name) {
|
||||
SDL_assert_always(action_name);
|
||||
|
||||
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
||||
if (action == NULL) {
|
||||
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
||||
return false;
|
||||
@ -278,8 +288,10 @@ bool input_is_action_pressed(InputState *input, char *action_name) {
|
||||
}
|
||||
|
||||
|
||||
bool input_is_action_just_pressed(InputState *input, char *action_name) {
|
||||
ActionHashItem *action = shgetp_null(input->action_hash, action_name);
|
||||
bool input_is_action_just_pressed(char const *action_name) {
|
||||
SDL_assert_always(action_name);
|
||||
|
||||
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
||||
if (action == NULL) {
|
||||
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
||||
return false;
|
||||
@ -288,8 +300,10 @@ bool input_is_action_just_pressed(InputState *input, char *action_name) {
|
||||
}
|
||||
|
||||
|
||||
bool input_is_action_just_released(InputState *input, char *action_name) {
|
||||
ActionHashItem *action = shgetp_null(input->action_hash, action_name);
|
||||
bool input_is_action_just_released(char const *action_name) {
|
||||
SDL_assert_always(action_name);
|
||||
|
||||
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
||||
if (action == NULL) {
|
||||
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
||||
return false;
|
||||
@ -298,8 +312,10 @@ bool input_is_action_just_released(InputState *input, char *action_name) {
|
||||
}
|
||||
|
||||
|
||||
Vec2 input_get_action_position(InputState *input, char *action_name) {
|
||||
ActionHashItem *action = shgetp_null(input->action_hash, action_name);
|
||||
Vec2 input_get_action_position(char const *action_name) {
|
||||
SDL_assert_always(action_name);
|
||||
|
||||
ActionHashItem *action = shgetp_null(ctx.input.action_hash, action_name);
|
||||
if (action == NULL) {
|
||||
log_warn("(%s) Action \"%s\" does not exist.", __func__, action_name);
|
||||
return (Vec2) { 0 };
|
||||
@ -309,15 +325,13 @@ Vec2 input_get_action_position(InputState *input, char *action_name) {
|
||||
}
|
||||
|
||||
|
||||
void input_set_mouse_captured(InputState *input, bool enabled) {
|
||||
(void)input;
|
||||
/* TODO: returns -1 if not supported, but like... do we care? */
|
||||
SDL_SetRelativeMouseMode(enabled);
|
||||
void input_set_mouse_captured(bool enabled) {
|
||||
if (SDL_SetRelativeMouseMode(enabled) != 0)
|
||||
log_warn("(%s) Mouse capture isn't supported.", __func__);
|
||||
}
|
||||
|
||||
|
||||
bool input_is_mouse_captured(InputState *input) {
|
||||
(void)input;
|
||||
bool input_is_mouse_captured(void) {
|
||||
return SDL_GetRelativeMouseMode();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifndef INPUT_INTERNAL_API_H
|
||||
#define INPUT_INTERNAL_API_H
|
||||
#ifndef TWN_INPUT_C_H
|
||||
#define TWN_INPUT_C_H
|
||||
|
||||
#include "twn_input.h"
|
||||
#include "twn_vec.h"
|
||||
@ -15,6 +15,15 @@ union ButtonCode {
|
||||
};
|
||||
|
||||
|
||||
typedef enum ButtonSource {
|
||||
BUTTON_SOURCE_NOT_SET,
|
||||
BUTTON_SOURCE_KEYBOARD_PHYSICAL,
|
||||
BUTTON_SOURCE_KEYBOARD_CHARACTER,
|
||||
BUTTON_SOURCE_GAMEPAD,
|
||||
BUTTON_SOURCE_MOUSE,
|
||||
} ButtonSource;
|
||||
|
||||
|
||||
/* an input to which an action is bound */
|
||||
/* it is not limited to literal buttons */
|
||||
typedef struct Button {
|
||||
@ -23,6 +32,45 @@ typedef struct Button {
|
||||
} Button;
|
||||
|
||||
|
||||
/* represents the collective state of a group of buttons */
|
||||
/* that is, changes in the states of any of the bound buttons will affect it */
|
||||
typedef struct Action {
|
||||
size_t num_bindings;
|
||||
|
||||
/* if you bind more than the number set in the configuration file */
|
||||
/* it forgets the first Button to add the new one at the end */
|
||||
Button *bindings;
|
||||
|
||||
Vec2 position; /* set if applicable, e.g. mouse click */
|
||||
bool is_pressed;
|
||||
bool just_changed;
|
||||
} Action;
|
||||
|
||||
|
||||
typedef struct ActionHashItem {
|
||||
char *key;
|
||||
Action value;
|
||||
} ActionHashItem;
|
||||
|
||||
|
||||
typedef struct InputState {
|
||||
const uint8_t *keyboard_state; /* array of booleans indexed by scancode */
|
||||
ActionHashItem *action_hash;
|
||||
Vec2i mouse_window_position;
|
||||
Vec2i mouse_relative_position;
|
||||
uint32_t mouse_state; /* SDL mouse button bitmask */
|
||||
ButtonSource last_active_source;
|
||||
bool is_anything_just_pressed;
|
||||
bool mouse_captured;
|
||||
} InputState;
|
||||
|
||||
|
||||
void input_state_init(InputState *input);
|
||||
|
||||
void input_state_deinit(InputState *input);
|
||||
|
||||
void input_state_update(InputState *input);
|
||||
|
||||
void input_reset_state(InputState *input);
|
||||
|
||||
#endif
|
||||
|
@ -188,7 +188,7 @@ static void main_loop(void) {
|
||||
|
||||
poll_events();
|
||||
|
||||
input_state_update(&ctx.game.input);
|
||||
input_state_update(&ctx.input);
|
||||
|
||||
game_object_tick();
|
||||
|
||||
@ -634,7 +634,7 @@ static bool initialize(void) {
|
||||
ctx.keybind_slots = datum_keybind_slots.u.i;
|
||||
}
|
||||
}
|
||||
input_state_init(&ctx.game.input);
|
||||
input_state_init(&ctx.input);
|
||||
|
||||
/* scripting */
|
||||
/*
|
||||
@ -657,7 +657,7 @@ static void clean_up(void) {
|
||||
scripting_deinit(ctx);
|
||||
*/
|
||||
|
||||
input_state_deinit(&ctx.game.input);
|
||||
input_state_deinit(&ctx.input);
|
||||
text_cache_deinit(&ctx.text_cache);
|
||||
textures_cache_deinit(&ctx.texture_cache);
|
||||
|
||||
@ -675,7 +675,7 @@ static void clean_up(void) {
|
||||
|
||||
|
||||
static void reset_state(void) {
|
||||
input_reset_state(&ctx.game.input);
|
||||
input_reset_state(&ctx.input);
|
||||
textures_reset_state();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user