From: notaz Date: Fri, 16 May 2025 21:04:40 +0000 (+0300) Subject: in_sdl: improve joy support X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e3c0b8868e93724929af74bbff624d0b7f076119;p=libpicofe.git in_sdl: improve joy support - avoid event flood for analog->udlr mapping - ability to disable this mapping - basic hat support --- diff --git a/in_sdl.c b/in_sdl.c index 3fdc01a..15c8c22 100644 --- a/in_sdl.c +++ b/in_sdl.c @@ -19,13 +19,20 @@ typedef unsigned long keybits_t; #define KEYBITS_WORD_BITS (sizeof(keybits_t) * 8) +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +#endif + struct in_sdl_state { const in_drv_t *drv; SDL_Joystick *joy; int joy_id; int numaxes; int axis_keydown[2]; - int redraw; + int hat_down; + unsigned int redraw:1; + unsigned int abs_to_udlr:1; + unsigned int hat_warned:1; SDL_Event revent; SDL_Event mevent; // last mouse event keybits_t keystate[SDLK_LAST / KEYBITS_WORD_BITS + 1]; @@ -169,6 +176,23 @@ static const char * const in_sdl_keys[SDLK_LAST] = { [SDLK_COMPOSE] = "compose", }; +static struct in_sdl_state *state_alloc(void) +{ + struct in_sdl_state *state; + size_t i; + + state = calloc(1, sizeof(*state)); + if (state == NULL) { + fprintf(stderr, "in_sdl: OOM\n"); + return NULL; + } + + state->abs_to_udlr = 1; + for (i = 0; i < ARRAY_SIZE(state->axis_keydown); i++) + state->axis_keydown[i] = -1; + return state; +} + static void in_sdl_probe(const in_drv_t *drv) { const struct in_pdata *pdata = drv->pdata; @@ -181,11 +205,8 @@ static void in_sdl_probe(const in_drv_t *drv) if (pdata->key_names) key_names = pdata->key_names; - state = calloc(1, sizeof(*state)); - if (state == NULL) { - fprintf(stderr, "in_sdl: OOM\n"); + if (!(state = state_alloc())) return; - } state->drv = drv; in_register(IN_SDL_PREFIX "keys", -1, state, SDLK_LAST, @@ -201,11 +222,8 @@ static void in_sdl_probe(const in_drv_t *drv) if (joy == NULL) continue; - state = calloc(1, sizeof(*state)); - if (state == NULL) { - fprintf(stderr, "in_sdl: OOM\n"); + if (!(state = state_alloc())) break; - } state->joy = joy; state->joy_id = i; state->numaxes = SDL_JoystickNumAxes(joy); @@ -289,42 +307,39 @@ static int handle_event(struct in_sdl_state *state, SDL_Event *event, static int handle_joy_event(struct in_sdl_state *state, SDL_Event *event, int *kc_out, int *down_out, int *emu_out) { - int kc = -1, down = 0, emu = 0, ret = 0; + static const int hat2key[4] = { SDLK_UP, SDLK_RIGHT, SDLK_DOWN, SDLK_LEFT }; + int kc = -1, kc_prev = -1, down = 0, emu = 0, ret = 0, i, val, xor; /* TODO: remaining axis */ switch (event->type) { case SDL_JOYAXISMOTION: + if (!state->abs_to_udlr) + return -1; if (event->jaxis.which != state->joy_id) return -2; if (event->jaxis.axis > 1) break; - if (-16384 <= event->jaxis.value && event->jaxis.value <= 16384) { - kc = state->axis_keydown[event->jaxis.axis]; - state->axis_keydown[event->jaxis.axis] = 0; - ret = 1; - } - else if (event->jaxis.value < -16384) { - kc = state->axis_keydown[event->jaxis.axis]; - if (kc) { - emu |= get_keystate(state->emu_keys, kc); - update_keystate(state->keystate, kc, 0); - } + if (event->jaxis.value < -16384) { kc = event->jaxis.axis ? SDLK_UP : SDLK_LEFT; - state->axis_keydown[event->jaxis.axis] = kc; down = 1; - ret = 1; } else if (event->jaxis.value > 16384) { - kc = state->axis_keydown[event->jaxis.axis]; - if (kc) { - emu |= get_keystate(state->emu_keys, kc); - update_keystate(state->keystate, kc, 0); - } kc = event->jaxis.axis ? SDLK_DOWN : SDLK_RIGHT; - state->axis_keydown[event->jaxis.axis] = kc; down = 1; - ret = 1; } + kc_prev = state->axis_keydown[event->jaxis.axis]; + state->axis_keydown[event->jaxis.axis] = kc; + if (kc == kc_prev) + return -1; // no simulated key change + if (kc >= 0 && kc_prev >= 0) { + // must release the old key first + state->axis_keydown[event->jaxis.axis] = -1; + kc = kc_prev; + down = 0; + } + else if (kc_prev >= 0) + kc = kc_prev; + ret = 1; break; case SDL_JOYBUTTONDOWN: @@ -335,7 +350,29 @@ static int handle_joy_event(struct in_sdl_state *state, SDL_Event *event, down = event->jbutton.state == SDL_PRESSED; ret = 1; break; + case SDL_JOYHATMOTION: + if (event->jhat.which != state->joy_id) + return -2; + if (event->jhat.hat != 0) + return -1; // todo? + xor = event->jhat.value ^ state->hat_down; + val = state->hat_down = event->jhat.value; + for (i = 0; i < 4; i++, xor >>= 1, val >>= 1) { + if (xor & 1) { + down = val & 1; + kc = hat2key[i]; + ret = 1; + break; + } + } + if ((!ret || (xor >> ret)) && !state->hat_warned) { + // none, more than 1, or upper bits changed + fprintf(stderr, "in_sdl: unexpected hat behavior\n"); + state->hat_warned = 1; + } + break; default: + //printf("joy ev %d\n", event->type); return -1; } @@ -366,7 +403,7 @@ static int collect_events(struct in_sdl_state *state, int *one_kc, int *one_down SDL_PumpEvents(); - maxcount = sizeof(events) / sizeof(events[0]); + maxcount = ARRAY_SIZE(events); if ((count = SDL_PeepEvents(events, maxcount, SDL_GETEVENT, mask)) > 0) { for (i = 0; i < count; i++) { event = &events[i]; @@ -628,6 +665,31 @@ static int in_sdl_get_config(void *drv_data, enum in_cfg_opt what, int *val) case IN_CFG_ABS_AXIS_COUNT: *val = state->numaxes; break; + case IN_CFG_ANALOG_MAP_ULDR: + *val = state->abs_to_udlr; + break; + default: + return -1; + } + + return 0; +} + +static int in_sdl_set_config(void *drv_data, enum in_cfg_opt what, int val) +{ + struct in_sdl_state *state = drv_data; + size_t i; + + switch (what) { + case IN_CFG_ANALOG_MAP_ULDR: + state->abs_to_udlr = val ? 1 : 0; + for (i = 0; !val && i < ARRAY_SIZE(state->axis_keydown); i++) { + if (state->axis_keydown[i] < 0) + continue; + update_keystate(state->keystate, state->axis_keydown[i], 0); + state->axis_keydown[i] = -1; + } + break; default: return -1; } @@ -648,6 +710,7 @@ static const in_drv_t in_sdl_drv = { .menu_translate = in_sdl_menu_translate, .clean_binds = in_sdl_clean_binds, .get_config = in_sdl_get_config, + .set_config = in_sdl_set_config, }; int in_sdl_init(const struct in_pdata *pdata, void (*handler)(void *event)) diff --git a/input.h b/input.h index 4fcdbde..5ac73c9 100644 --- a/input.h +++ b/input.h @@ -61,6 +61,7 @@ enum in_cfg_opt { IN_CFG_ABS_DEAD_ZONE, /* dead zone for analog-digital mapping */ IN_CFG_ABS_AXIS_COUNT, /* number of abs axes (ro) */ IN_CFG_DEFAULT_DEV, + IN_CFG_ANALOG_MAP_ULDR, }; enum { diff --git a/linux/in_evdev.c b/linux/in_evdev.c index 49bfa37..b3fb825 100644 --- a/linux/in_evdev.c +++ b/linux/in_evdev.c @@ -437,6 +437,8 @@ static int in_evdev_get_config(void *drv_data, enum in_cfg_opt what, int *val) case IN_CFG_ABS_AXIS_COUNT: *val = dev->abs_count; break; + case IN_CFG_ANALOG_MAP_ULDR: + *val = dev->abs_to_digital; default: return -1; } @@ -463,6 +465,8 @@ static int in_evdev_set_config(void *drv_data, enum in_cfg_opt what, int val) else if (dev->abs_lzone >= tmp) dev->abs_lzone = tmp - 1; break; + case IN_CFG_ANALOG_MAP_ULDR: + dev->abs_to_digital = val; default: return -1; }