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];
[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;
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,
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);
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:
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;
}
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];
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;
}
.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))