gamepad: implement button repeat

This commit is contained in:
Andrei Alexeyev 2018-01-16 13:51:19 +02:00
parent a5b70f4a2a
commit 78e9d3cb9e
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
4 changed files with 65 additions and 6 deletions

View file

@ -103,6 +103,8 @@
CONFIGDEF_FLOAT (GAMEPAD_AXIS_UD_SENS, "gamepad_axis_ud_free_sensitivity", 1.0) \
CONFIGDEF_FLOAT (GAMEPAD_AXIS_LR_SENS, "gamepad_axis_lr_free_sensitivity", 1.0) \
CONFIGDEF_FLOAT (GAMEPAD_AXIS_DEADZONE, "gamepad_axis_deadzone", 0.1) \
CONFIGDEF_FLOAT (GAMEPAD_BTNREPEAT_DELAY, "gamepad_button_repeat_delay", 0.25) \
CONFIGDEF_FLOAT (GAMEPAD_BTNREPEAT_INTERVAL,"gamepad_button_repeat_interval", 0.02) \
GPKEYDEFS \

View file

@ -226,6 +226,7 @@ static void events_apply_flags(EventFlags flags) {
void events_poll(EventHandler *handlers, EventFlags flags) {
SDL_Event event;
events_apply_flags(flags);
events_emit(TE_FRAME, 0, NULL, NULL);
while(SDL_PollEvent(&event)) {
events_invoke_handlers(&event, global_handlers, handlers);

View file

@ -12,6 +12,8 @@
#include "util.h"
typedef enum {
TE_FRAME,
TE_RESOURCE_ASYNC_LOADED,
#define TE_MENU_FIRST TE_MENU_CURSOR_UP

View file

@ -12,6 +12,7 @@
#include "config.h"
#include "events.h"
#include "global.h"
#include "hirestime.h"
typedef struct GamepadAxisState {
int16_t raw;
@ -19,12 +20,19 @@ typedef struct GamepadAxisState {
int8_t digital;
} GamepadAxisState;
typedef struct GamepadButtonState {
bool held;
hrtime_t repeat_time;
} GamepadButtonState;
static struct {
bool initialized;
int current_devnum;
SDL_GameController *device;
SDL_JoystickID instance;
GamepadAxisState *axes;
GamepadButtonState *buttons;
struct {
int *id_map;
@ -252,6 +260,7 @@ void gamepad_init(void) {
SDL_Joystick *joy = SDL_GameControllerGetJoystick(gamepad.device);
gamepad.instance = SDL_JoystickInstanceID(joy);
gamepad.axes = calloc(GAMEPAD_AXIS_MAX, sizeof(GamepadAxisState));
gamepad.buttons = calloc(GAMEPAD_BUTTON_MAX + GAMEPAD_EMULATED_BUTTON_MAX, sizeof(GamepadButtonState));
log_info("Using device '%s' (#%i): %s", guid, dev, gamepad_device_name(dev));
SDL_GameControllerEventState(SDL_ENABLE);
@ -411,7 +420,7 @@ int gamepad_player_axis_value(GamepadPlrAxis paxis) {
return gamepad_axis_value(id);
}
static void gamepad_button(GamepadButton button, int state);
static void gamepad_button(GamepadButton button, int state, bool is_repeat);
static void gamepad_axis(GamepadAxis id, int raw);
double gamepad_normalize_axis_value(int val) {
@ -529,7 +538,7 @@ static void gamepad_axis(GamepadAxis id, int raw) {
GamepadButton btn = gamepad_button_from_axis(id);
if(btn != GAMEPAD_BUTTON_INVALID) {
gamepad_button(btn, SDL_PRESSED);
gamepad_button(btn, SDL_PRESSED, false);
}
}
} else if(gamepad.axes[id].digital != AXISVAL_NULL) { // simulate release
@ -538,17 +547,41 @@ static void gamepad_axis(GamepadAxis id, int raw) {
GamepadButton btn = gamepad_button_from_axis(id);
if(btn != GAMEPAD_BUTTON_INVALID) {
gamepad_button(btn, SDL_RELEASED);
gamepad_button(btn, SDL_RELEASED, false);
}
}
}
static void gamepad_button(GamepadButton button, int state) {
static GamepadButtonState* gamepad_button_state(GamepadButton button) {
if(button > GAMEPAD_BUTTON_INVALID && button < GAMEPAD_BUTTON_MAX) {
return gamepad.buttons + button;
}
if(button & GAMEPAD_BUTTON_EMULATED) {
GamepadEmulatedButton ebutton = button & ~GAMEPAD_BUTTON_EMULATED;
if(ebutton > GAMEPAD_EMULATED_BUTTON_INVALID && ebutton < GAMEPAD_EMULATED_BUTTON_MAX) {
return gamepad.buttons + GAMEPAD_BUTTON_MAX + ebutton;
}
}
log_fatal("Button id %i is invalid", button);
}
static void gamepad_button(GamepadButton button, int state, bool is_repeat) {
int gpkey = config_gamepad_key_from_gamepad_button(button);
int key = config_key_from_gamepad_key(gpkey);
void *indev = (void*)(intptr_t)INDEV_GAMEPAD;
GamepadButtonState *btnstate = gamepad_button_state(button);
if(state == SDL_PRESSED) {
if(is_repeat) {
btnstate->repeat_time = time_get() + config_get_float(CONFIG_GAMEPAD_BTNREPEAT_INTERVAL);
} else {
btnstate->repeat_time = time_get() + config_get_float(CONFIG_GAMEPAD_BTNREPEAT_DELAY);
}
btnstate->held = true;
events_emit(TE_GAMEPAD_BUTTON_DOWN, button, indev, NULL);
switch(button) {
@ -579,10 +612,11 @@ static void gamepad_button(GamepadButton button, int state) {
break;
}
if(key >= 0) {
if(key >= 0 && !is_repeat) {
events_emit(TE_GAME_KEY_DOWN, key, indev, NULL);
}
} else {
btnstate->held = false;
events_emit(TE_GAMEPAD_BUTTON_UP, button, indev, NULL);
if(key >= 0) {
@ -591,6 +625,14 @@ static void gamepad_button(GamepadButton button, int state) {
}
}
static void gamepad_handle_button_repeat(GamepadButton btn, hrtime_t time) {
GamepadButtonState *state = gamepad_button_state(btn);
if(state->held && time >= state->repeat_time) {
gamepad_button(btn, SDL_PRESSED, true);
}
}
static bool gamepad_event_handler(SDL_Event *event, void *arg) {
assert(gamepad.initialized);
@ -611,12 +653,24 @@ static bool gamepad_event_handler(SDL_Event *event, void *arg) {
GamepadButton btn = gamepad_button_from_sdl_button(event->cbutton.button);
if(btn != GAMEPAD_BUTTON_INVALID) {
gamepad_button(btn, event->cbutton.state);
gamepad_button(btn, event->cbutton.state, false);
}
}
return true;
}
if(event->type == MAKE_TAISEI_EVENT(TE_FRAME)) {
hrtime_t time = time_get();
for(GamepadButton btn = 0; btn < GAMEPAD_BUTTON_MAX; ++btn) {
gamepad_handle_button_repeat(btn, time);
}
for(GamepadEmulatedButton btn = 0; btn < GAMEPAD_EMULATED_BUTTON_MAX; ++btn) {
gamepad_handle_button_repeat(btn | GAMEPAD_BUTTON_EMULATED, time);
}
}
return false;
}