2 * (C) GraÅžvydas "notaz" Ignotas, 2012
4 * This work is licensed under the terms of any of these licenses
6 * - GNU GPL, version 2 or later.
7 * - GNU LGPL, version 2.1 or later.
9 * See the COPYING file in the top-level directory.
17 #define IN_SDL_PREFIX "sdl:"
18 /* should be machine word for best performace */
19 typedef unsigned long keybits_t;
20 #define KEYBITS_WORD_BITS (sizeof(keybits_t) * 8)
23 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
32 int *joy_axis_keydown;
34 unsigned int joy_axis_as_btn; // bitmask; vs axes of centered sticks
35 unsigned int redraw:1;
36 unsigned int abs_to_udlr:1;
37 unsigned int hat_warned:1;
39 SDL_Event mevent; // last mouse event
40 keybits_t keystate[SDLK_LAST / KEYBITS_WORD_BITS + 1];
41 // emulator keys should always be processed immediately lest one is lost
42 keybits_t emu_keys[SDLK_LAST / KEYBITS_WORD_BITS + 1];
45 static void (*ext_event_handler)(void *event);
47 static const char * const in_sdl_keys[SDLK_LAST] = {
48 [SDLK_BACKSPACE] = "backspace",
50 [SDLK_CLEAR] = "clear",
51 [SDLK_RETURN] = "return",
52 [SDLK_PAUSE] = "pause",
53 [SDLK_ESCAPE] = "escape",
54 [SDLK_SPACE] = "space",
56 [SDLK_QUOTEDBL] = "\"",
59 [SDLK_AMPERSAND] = "&",
61 [SDLK_LEFTPAREN] = "(",
62 [SDLK_RIGHTPAREN] = ")",
63 [SDLK_ASTERISK] = "*",
80 [SDLK_SEMICOLON] = ";",
84 [SDLK_QUESTION] = "?",
86 [SDLK_LEFTBRACKET] = "[",
87 [SDLK_BACKSLASH] = "\\",
88 [SDLK_RIGHTBRACKET] = "]",
90 [SDLK_UNDERSCORE] = "_",
91 [SDLK_BACKQUOTE] = "`",
118 [SDLK_DELETE] = "delete",
121 #define B(x) [SDLK_WORLD_##x] = "b"#x
122 B(0), B(1), B(2), B(3), B(4), B(5), B(6), B(7),
123 B(8), B(9), B(10), B(11), B(12), B(13), B(14), B(15),
124 B(16), B(17), B(18), B(19), B(20), B(21), B(22), B(23),
125 B(24), B(25), B(26), B(27), B(28), B(29), B(30), B(31),
126 B(32), B(33), B(34), B(35), B(36), B(37), B(38), B(39),
139 [SDLK_KP_PERIOD] = "[.]",
140 [SDLK_KP_DIVIDE] = "[/]",
141 [SDLK_KP_MULTIPLY] = "[*]",
142 [SDLK_KP_MINUS] = "[-]",
143 [SDLK_KP_PLUS] = "[+]",
144 [SDLK_KP_ENTER] = "enter",
145 [SDLK_KP_EQUALS] = "equals",
148 [SDLK_DOWN] = "down",
149 [SDLK_RIGHT] = "right",
150 [SDLK_LEFT] = "left",
151 [SDLK_INSERT] = "insert",
152 [SDLK_HOME] = "home",
154 [SDLK_PAGEUP] = "page up",
155 [SDLK_PAGEDOWN] = "page down",
173 [SDLK_NUMLOCK] = "numlock",
174 [SDLK_CAPSLOCK] = "caps lock",
175 [SDLK_SCROLLOCK] = "scroll lock",
176 [SDLK_RSHIFT] = "right shift",
177 [SDLK_LSHIFT] = "left shift",
178 [SDLK_RCTRL] = "right ctrl",
179 [SDLK_LCTRL] = "left ctrl",
180 [SDLK_RALT] = "right alt",
181 [SDLK_LALT] = "left alt",
182 [SDLK_RMETA] = "right meta",
183 [SDLK_LMETA] = "left meta",
184 [SDLK_LSUPER] = "left super", /* "Windows" keys */
185 [SDLK_RSUPER] = "right super",
186 [SDLK_MODE] = "alt gr",
187 [SDLK_COMPOSE] = "compose",
190 static struct in_sdl_state *state_alloc(int joy_numaxes)
192 struct in_sdl_state *state;
194 state = calloc(1, sizeof(*state));
196 fprintf(stderr, "in_sdl: OOM\n");
201 state->abs_to_udlr = 1;
202 state->joy_numaxes = joy_numaxes;
203 state->joy_axis_keydown = malloc(joy_numaxes * sizeof(state->joy_axis_keydown[0]));
204 if (!state->joy_axis_keydown) {
208 memset(state->joy_axis_keydown, 0xff, joy_numaxes * sizeof(state->joy_axis_keydown[0]));
213 static void in_sdl_probe(const in_drv_t *drv)
215 const struct in_pdata *pdata = drv->pdata;
216 const char * const * key_names = in_sdl_keys;
217 struct in_sdl_state *state;
222 if (pdata->key_names)
223 key_names = pdata->key_names;
225 if (!(state = state_alloc(0)))
229 in_register(IN_SDL_PREFIX "keys", -1, state, SDLK_LAST,
231 //SDL_EnableUNICODE(1);
233 /* joysticks go here too */
234 SDL_InitSubSystem(SDL_INIT_JOYSTICK);
236 joycount = SDL_NumJoysticks();
237 for (i = 0; i < joycount; i++) {
238 joy = SDL_JoystickOpen(i);
242 if (!(state = state_alloc(SDL_JoystickNumAxes(joy))))
246 state->joy_numbuttons = SDL_JoystickNumButtons(joy);
248 for (a = 0; a < state->joy_numaxes; a++)
249 if (SDL_JoystickGetAxis(joy, a) < -16384)
250 state->joy_axis_as_btn |= 1u << a;
252 snprintf(name, sizeof(name), IN_SDL_PREFIX "%s", SDL_JoystickName(i));
253 in_register(name, -1, state, SDLK_LAST, key_names, 0);
255 printf(" %s: %d buttons %d axes %d hat(s), "
256 "guessed axis_as_btn mask: %x\n",
257 name, state->joy_numbuttons, state->joy_numaxes,
258 SDL_JoystickNumHats(joy), state->joy_axis_as_btn);
262 SDL_JoystickEventState(SDL_ENABLE);
265 static void in_sdl_free(void *drv_data)
267 struct in_sdl_state *state = drv_data;
270 if (state->joy != NULL)
271 SDL_JoystickClose(state->joy);
272 free(state->joy_axis_keydown);
277 static const char * const *
278 in_sdl_get_key_names(const in_drv_t *drv, int *count)
280 const struct in_pdata *pdata = drv->pdata;
283 if (pdata->key_names)
284 return pdata->key_names;
288 /* could use SDL_GetKeyState, but this gives better packing */
289 static void update_keystate(keybits_t *keystate, int sym, int is_down)
291 keybits_t *ks_word, mask;
294 mask <<= sym & (KEYBITS_WORD_BITS - 1);
295 ks_word = keystate + sym / KEYBITS_WORD_BITS;
302 static int get_keystate(keybits_t *keystate, int sym)
304 keybits_t *ks_word, mask;
307 mask <<= sym & (KEYBITS_WORD_BITS - 1);
308 ks_word = keystate + sym / KEYBITS_WORD_BITS;
309 return !!(*ks_word & mask);
312 static int handle_event(struct in_sdl_state *state, SDL_Event *event,
313 int *kc_out, int *down_out, int *emu_out)
317 if (event->type != SDL_KEYDOWN && event->type != SDL_KEYUP)
320 emu = get_keystate(state->emu_keys, event->key.keysym.sym);
321 update_keystate(state->keystate, event->key.keysym.sym,
322 event->type == SDL_KEYDOWN);
324 *kc_out = event->key.keysym.sym;
325 if (down_out != NULL)
326 *down_out = event->type == SDL_KEYDOWN;
333 static int handle_joy_event(struct in_sdl_state *state, SDL_Event *event,
334 int *kc_out, int *down_out, int *emu_out)
336 static const int hat2uldr[4] = { SDLK_UP, SDLK_RIGHT, SDLK_DOWN, SDLK_LEFT };
337 int kc = -1, kc_prev = -1, down = 0, emu = 0, ret = 0, i, val, xor;
338 int kc_button_base = SDLK_WORLD_0;
339 int kc_axis_base = kc_button_base + state->joy_numbuttons;
340 int kc_hat_base = kc_axis_base + state->joy_numaxes;
342 switch (event->type) {
343 case SDL_JOYAXISMOTION:
344 if ((unsigned)event->jaxis.axis >= (unsigned)state->joy_numaxes)
346 if (event->jaxis.which != state->joy_id)
348 if (event->jaxis.value < -16384) {
349 if (state->abs_to_udlr && event->jaxis.axis < 2)
350 kc = event->jaxis.axis ? SDLK_UP : SDLK_LEFT;
351 // some pressure sensitive buttons appear as an axis that goes
352 // from -32768 (released) to 32767 (pressed) and there is no
353 // way to distinguish from ones centered at 0? :(
354 else if (!(state->joy_axis_as_btn & (1u << event->jaxis.axis)))
355 kc = kc_axis_base + event->jaxis.axis * 2;
358 else if (event->jaxis.value > 16384) {
359 if (state->abs_to_udlr && event->jaxis.axis < 2)
360 kc = event->jaxis.axis ? SDLK_DOWN : SDLK_RIGHT;
362 kc = kc_axis_base + event->jaxis.axis * 2 + 1;
365 kc_prev = state->joy_axis_keydown[event->jaxis.axis];
366 state->joy_axis_keydown[event->jaxis.axis] = kc;
368 return -1; // no simulated key change
369 if (kc >= 0 && kc_prev >= 0) {
370 // must release the old key first
371 state->joy_axis_keydown[event->jaxis.axis] = -1;
375 else if (kc_prev >= 0)
380 case SDL_JOYBUTTONDOWN:
381 case SDL_JOYBUTTONUP:
382 if (event->jbutton.which != state->joy_id)
384 kc = kc_button_base + (int)event->jbutton.button;
385 down = event->jbutton.state == SDL_PRESSED;
388 case SDL_JOYHATMOTION:
389 if (event->jhat.which != state->joy_id)
391 xor = event->jhat.value ^ state->joy_hat_down;
392 val = state->joy_hat_down = event->jhat.value;
393 for (i = 0; i < 4; i++, xor >>= 1, val >>= 1) {
396 kc = kc_hat_base + event->jhat.hat * 4 + i;
404 if ((!ret || (xor >> ret)) && !state->hat_warned) {
405 // none, more than 1, or upper bits changed
406 fprintf(stderr, "in_sdl: unexpected hat behavior\n");
407 state->hat_warned = 1;
411 //printf("joy ev %d\n", event->type);
416 emu |= get_keystate(state->emu_keys, kc);
417 update_keystate(state->keystate, kc, down);
421 if (down_out != NULL)
429 #define JOY_EVENTS (SDL_JOYAXISMOTIONMASK | SDL_JOYBALLMOTIONMASK | SDL_JOYHATMOTIONMASK \
430 | SDL_JOYBUTTONDOWNMASK | SDL_JOYBUTTONUPMASK)
432 static int collect_events(struct in_sdl_state *state, int *one_kc, int *one_down)
435 Uint32 mask = state->joy ? JOY_EVENTS : (SDL_ALLEVENTS & ~JOY_EVENTS);
436 int count, maxcount, is_emukey = 0;
437 int i = 0, ret = 0, retval = 0;
442 maxcount = ARRAY_SIZE(events);
443 if ((count = SDL_PeepEvents(events, maxcount, SDL_GETEVENT, mask)) > 0) {
444 for (i = 0; i < count; i++) {
447 ret = handle_joy_event(state,
448 event, one_kc, one_down, &is_emukey);
450 ret = handle_event(state,
451 event, one_kc, one_down, &is_emukey);
456 SDL_PushEvent(event);
459 if (event->type == SDL_VIDEORESIZE) {
461 state->revent = *event;
462 } else if (event->type == SDL_VIDEOEXPOSE) {
463 if (state->revent.type == SDL_NOEVENT) {
465 state->revent.type = SDL_VIDEOEXPOSE;
468 if ((event->type == SDL_MOUSEBUTTONDOWN) ||
469 (event->type == SDL_MOUSEBUTTONUP)) {
470 int mask = SDL_BUTTON(event->button.button);
471 if (event->button.state == SDL_PRESSED)
472 state->mevent.motion.state |= mask;
473 else state->mevent.motion.state &= ~mask;
474 } else if (event->type == SDL_MOUSEMOTION) {
475 event->motion.xrel += state->mevent.motion.xrel;
476 event->motion.yrel += state->mevent.motion.yrel;
477 state->mevent = *event;
479 else if (ext_event_handler != NULL)
480 ext_event_handler(event);
487 if ((is_emukey || one_kc != NULL) && retval)
494 // if the event queue has been emptied and resize/expose events were in it
495 if (state->redraw && count == 0) {
496 if (ext_event_handler != NULL)
497 ext_event_handler(&state->revent);
499 state->revent.type = SDL_NOEVENT;
500 // dummy key event to force returning from the key loop,
501 // so the application has a chance to redraw the window
502 if (one_kc != NULL) {
503 *one_kc = SDLK_UNKNOWN;
506 if (one_down != NULL)
510 // don't lose events other devices might want to handle
512 SDL_PeepEvents(events+i, count-i, SDL_ADDEVENT, mask);
516 static int in_sdl_update(void *drv_data, const int *binds, int *result)
518 struct in_sdl_state *state = drv_data;
522 collect_events(state, NULL, NULL);
524 for (i = 0; i < SDLK_LAST / KEYBITS_WORD_BITS + 1; i++) {
525 mask = state->keystate[i];
528 for (bit = 0; mask != 0; bit++, mask >>= 1) {
531 sym = i * KEYBITS_WORD_BITS + bit;
533 for (b = 0; b < IN_BINDTYPE_COUNT; b++)
534 result[b] |= binds[IN_BIND_OFFS(sym, b)];
541 static int in_sdl_update_kbd(void *drv_data, const int *binds, int *result)
543 struct in_sdl_state *state = drv_data;
545 int i, sym, bit, b = 0;
547 collect_events(state, NULL, NULL);
549 for (i = 0; i < SDLK_LAST / KEYBITS_WORD_BITS + 1; i++) {
550 mask = state->keystate[i];
553 for (bit = 0; mask != 0; bit++, mask >>= 1) {
556 sym = i * KEYBITS_WORD_BITS + bit;
557 result[b++] = binds[sym];
564 static int in_sdl_update_analog(void *drv_data, int axis_id, int *result)
566 struct in_sdl_state *state = drv_data;
569 if (!state || !state->joy || !result)
571 if ((unsigned)axis_id >= (unsigned)state->joy_numaxes)
574 v = SDL_JoystickGetAxis(state->joy, axis_id);
576 // -32768...32767 -> -IN_ABS_RANGE...IN_ABS_RANGE
577 *result = (v + ((v >> 31) | 1)) / (32768 / IN_ABS_RANGE);
581 static int in_sdl_update_pointer(void *drv_data, int id, int *result)
583 struct in_sdl_state *state = drv_data;
589 // absolute position, clipped at the window/screen border
590 case 0: if ((max = state->revent.resize.w))
591 *result = state->mevent.motion.x * 2*1024/max - 1024;
593 case 1: if ((max = state->revent.resize.h))
594 *result = state->mevent.motion.y * 2*1024/max - 1024;
596 // relative mouse movements since last query
597 case 2: if ((max = state->revent.resize.w))
598 *result = state->mevent.motion.xrel * 2*1024/max;
599 state->mevent.motion.xrel = 0;
601 case 3: if ((max = state->revent.resize.h))
602 *result = state->mevent.motion.yrel * 2*1024/max;
603 state->mevent.motion.yrel = 0;
606 case -1: *result = state->mevent.motion.state;
614 static int in_sdl_update_keycode(void *drv_data, int *is_down)
616 struct in_sdl_state *state = drv_data;
617 int ret_kc = -1, ret_down = 0;
619 collect_events(state, &ret_kc, &ret_down);
627 static int in_sdl_menu_translate(void *drv_data, int keycode, char *charcode)
629 struct in_sdl_state *state = drv_data;
630 const struct in_pdata *pdata = state->drv->pdata;
631 const char * const * key_names = in_sdl_keys;
632 const struct menu_keymap *map;
637 if (pdata->key_names)
638 key_names = pdata->key_names;
641 map = pdata->joy_map;
642 map_len = pdata->jmap_size;
644 map = pdata->key_map;
645 map_len = pdata->kmap_size;
652 for (i = 0; i < map_len; i++)
653 if (map[i].pbtn == keycode)
658 if (keycode == SDLK_UNKNOWN)
661 for (i = 0; i < map_len; i++) {
662 if (map[i].key == keycode) {
668 if (charcode != NULL && (unsigned int)keycode < SDLK_LAST &&
669 key_names[keycode] != NULL && key_names[keycode][1] == 0)
672 *charcode = key_names[keycode][0];
679 static int in_sdl_clean_binds(void *drv_data, int *binds, int *def_binds)
681 struct in_sdl_state *state = drv_data;
684 memset(state->emu_keys, 0, sizeof(state->emu_keys));
685 for (t = 0; t < IN_BINDTYPE_COUNT; t++) {
686 for (i = 0; i < SDLK_LAST; i++) {
687 int offs = IN_BIND_OFFS(i, t);
688 if (state->joy && i < SDLK_WORLD_0)
689 binds[offs] = def_binds[offs] = 0;
691 if (t == IN_BINDTYPE_EMU)
692 update_keystate(state->emu_keys, i, 1);
701 static int in_sdl_get_config(void *drv_data, enum in_cfg_opt what, int *val)
703 struct in_sdl_state *state = drv_data;
706 case IN_CFG_ABS_AXIS_COUNT:
707 *val = state->joy_numaxes;
709 case IN_CFG_ANALOG_MAP_ULDR:
710 *val = state->abs_to_udlr;
719 static int in_sdl_set_config(void *drv_data, enum in_cfg_opt what, int val)
721 struct in_sdl_state *state = drv_data;
725 case IN_CFG_ANALOG_MAP_ULDR:
726 state->abs_to_udlr = val ? 1 : 0;
727 for (i = 0; !val && i < state->joy_numaxes; i++) {
728 if (state->joy_axis_keydown[i] < 0)
730 update_keystate(state->keystate, state->joy_axis_keydown[i], 0);
731 state->joy_axis_keydown[i] = -1;
741 static const in_drv_t in_sdl_drv = {
742 .prefix = IN_SDL_PREFIX,
743 .probe = in_sdl_probe,
745 .get_key_names = in_sdl_get_key_names,
746 .update = in_sdl_update,
747 .update_kbd = in_sdl_update_kbd,
748 .update_analog = in_sdl_update_analog,
749 .update_pointer = in_sdl_update_pointer,
750 .update_keycode = in_sdl_update_keycode,
751 .menu_translate = in_sdl_menu_translate,
752 .clean_binds = in_sdl_clean_binds,
753 .get_config = in_sdl_get_config,
754 .set_config = in_sdl_set_config,
757 int in_sdl_init(const struct in_pdata *pdata, void (*handler)(void *event))
760 fprintf(stderr, "in_sdl: Missing input platform data\n");
764 in_register_driver(&in_sdl_drv, pdata->defbinds, pdata->kbd_map, pdata);
765 ext_event_handler = handler;