93fe22dad26ce5b7d4ab41a8ba06bf7088ff05bc
[libpicofe.git] / in_sdl.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2012
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
6  *  - GNU GPL, version 2 or later.
7  *  - GNU LGPL, version 2.1 or later.
8  *  - MAME license.
9  * See the COPYING file in the top-level directory.
10  */
11
12 #include <stdio.h>
13 #include <SDL.h>
14 #include "input.h"
15 #include "in_sdl.h"
16
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)
21
22 #ifndef ARRAY_SIZE
23 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
24 #endif
25
26 struct in_sdl_state {
27         const in_drv_t *drv;
28         SDL_Joystick *joy;
29         int joy_id;
30         int joy_numaxes;
31         int joy_numbuttons;
32         int *joy_axis_keydown;
33         int joy_hat_down;
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;
38         SDL_Event revent;
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];
43 };
44
45 static void (*ext_event_handler)(void *event);
46
47 static const char * const in_sdl_keys[SDLK_LAST] = {
48         [SDLK_BACKSPACE] = "backspace",
49         [SDLK_TAB] = "tab",
50         [SDLK_CLEAR] = "clear",
51         [SDLK_RETURN] = "return",
52         [SDLK_PAUSE] = "pause",
53         [SDLK_ESCAPE] = "escape",
54         [SDLK_SPACE] = "space",
55         [SDLK_EXCLAIM]  = "!",
56         [SDLK_QUOTEDBL]  = "\"",
57         [SDLK_HASH]  = "#",
58         [SDLK_DOLLAR]  = "$",
59         [SDLK_AMPERSAND]  = "&",
60         [SDLK_QUOTE] = "'",
61         [SDLK_LEFTPAREN] = "(",
62         [SDLK_RIGHTPAREN] = ")",
63         [SDLK_ASTERISK] = "*",
64         [SDLK_PLUS] = "+",
65         [SDLK_COMMA] = ",",
66         [SDLK_MINUS] = "-",
67         [SDLK_PERIOD] = ".",
68         [SDLK_SLASH] = "/",
69         [SDLK_0] = "0",
70         [SDLK_1] = "1",
71         [SDLK_2] = "2",
72         [SDLK_3] = "3",
73         [SDLK_4] = "4",
74         [SDLK_5] = "5",
75         [SDLK_6] = "6",
76         [SDLK_7] = "7",
77         [SDLK_8] = "8",
78         [SDLK_9] = "9",
79         [SDLK_COLON] = ":",
80         [SDLK_SEMICOLON] = ";",
81         [SDLK_LESS] = "<",
82         [SDLK_EQUALS] = "=",
83         [SDLK_GREATER] = ">",
84         [SDLK_QUESTION] = "?",
85         [SDLK_AT] = "@",
86         [SDLK_LEFTBRACKET] = "[",
87         [SDLK_BACKSLASH] = "\\",
88         [SDLK_RIGHTBRACKET] = "]",
89         [SDLK_CARET] = "^",
90         [SDLK_UNDERSCORE] = "_",
91         [SDLK_BACKQUOTE] = "`",
92         [SDLK_a] = "a",
93         [SDLK_b] = "b",
94         [SDLK_c] = "c",
95         [SDLK_d] = "d",
96         [SDLK_e] = "e",
97         [SDLK_f] = "f",
98         [SDLK_g] = "g",
99         [SDLK_h] = "h",
100         [SDLK_i] = "i",
101         [SDLK_j] = "j",
102         [SDLK_k] = "k",
103         [SDLK_l] = "l",
104         [SDLK_m] = "m",
105         [SDLK_n] = "n",
106         [SDLK_o] = "o",
107         [SDLK_p] = "p",
108         [SDLK_q] = "q",
109         [SDLK_r] = "r",
110         [SDLK_s] = "s",
111         [SDLK_t] = "t",
112         [SDLK_u] = "u",
113         [SDLK_v] = "v",
114         [SDLK_w] = "w",
115         [SDLK_x] = "x",
116         [SDLK_y] = "y",
117         [SDLK_z] = "z",
118         [SDLK_DELETE] = "delete",
119
120         // gamepad buttons
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),
127         #undef B
128
129         [SDLK_KP0] = "[0]",
130         [SDLK_KP1] = "[1]",
131         [SDLK_KP2] = "[2]",
132         [SDLK_KP3] = "[3]",
133         [SDLK_KP4] = "[4]",
134         [SDLK_KP5] = "[5]",
135         [SDLK_KP6] = "[6]",
136         [SDLK_KP7] = "[7]",
137         [SDLK_KP8] = "[8]",
138         [SDLK_KP9] = "[9]",
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",
146
147         [SDLK_UP] = "up",
148         [SDLK_DOWN] = "down",
149         [SDLK_RIGHT] = "right",
150         [SDLK_LEFT] = "left",
151         [SDLK_INSERT] = "insert",
152         [SDLK_HOME] = "home",
153         [SDLK_END] = "end",
154         [SDLK_PAGEUP] = "page up",
155         [SDLK_PAGEDOWN] = "page down",
156
157         [SDLK_F1] = "f1",
158         [SDLK_F2] = "f2",
159         [SDLK_F3] = "f3",
160         [SDLK_F4] = "f4",
161         [SDLK_F5] = "f5",
162         [SDLK_F6] = "f6",
163         [SDLK_F7] = "f7",
164         [SDLK_F8] = "f8",
165         [SDLK_F9] = "f9",
166         [SDLK_F10] = "f10",
167         [SDLK_F11] = "f11",
168         [SDLK_F12] = "f12",
169         [SDLK_F13] = "f13",
170         [SDLK_F14] = "f14",
171         [SDLK_F15] = "f15",
172
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",
188 };
189
190 static struct in_sdl_state *state_alloc(int joy_numaxes)
191 {
192         struct in_sdl_state *state;
193
194         state = calloc(1, sizeof(*state));
195         if (state == NULL) {
196                 fprintf(stderr, "in_sdl: OOM\n");
197                 return NULL;
198         }
199
200         if (joy_numaxes) {
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) {
205                         free(state);
206                         return NULL;
207                 }
208                 memset(state->joy_axis_keydown, 0xff, joy_numaxes * sizeof(state->joy_axis_keydown[0]));
209         }
210         return state;
211 }
212
213 static void in_sdl_probe(const in_drv_t *drv)
214 {
215         const struct in_pdata *pdata = drv->pdata;
216         const char * const * key_names = in_sdl_keys;
217         struct in_sdl_state *state;
218         SDL_Joystick *joy;
219         int i, a, joycount;
220         char name[256];
221
222         if (pdata->key_names)
223                 key_names = pdata->key_names;
224
225         if (!(state = state_alloc(0)))
226                 return;
227
228         state->drv = drv;
229         in_register(IN_SDL_PREFIX "keys", -1, state, SDLK_LAST,
230                 key_names, 0);
231         //SDL_EnableUNICODE(1);
232
233         /* joysticks go here too */
234         SDL_InitSubSystem(SDL_INIT_JOYSTICK);
235
236         joycount = SDL_NumJoysticks();
237         for (i = 0; i < joycount; i++) {
238                 joy = SDL_JoystickOpen(i);
239                 if (joy == NULL)
240                         continue;
241
242                 if (!(state = state_alloc(SDL_JoystickNumAxes(joy))))
243                         break;
244                 state->joy = joy;
245                 state->joy_id = i;
246                 state->joy_numbuttons = SDL_JoystickNumButtons(joy);
247                 state->drv = drv;
248                 for (a = 0; a < state->joy_numaxes; a++)
249                         if (SDL_JoystickGetAxis(joy, a) < -16384)
250                                 state->joy_axis_as_btn |= 1u << a;
251
252                 snprintf(name, sizeof(name), IN_SDL_PREFIX "%s", SDL_JoystickName(i));
253                 in_register(name, -1, state, SDLK_LAST, key_names, 0);
254
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);
259         }
260
261         if (joycount > 0)
262                 SDL_JoystickEventState(SDL_ENABLE);
263 }
264
265 static void in_sdl_free(void *drv_data)
266 {
267         struct in_sdl_state *state = drv_data;
268
269         if (state != NULL) {
270                 if (state->joy != NULL)
271                         SDL_JoystickClose(state->joy);
272                 free(state->joy_axis_keydown);
273                 free(state);
274         }
275 }
276
277 static const char * const *
278 in_sdl_get_key_names(const in_drv_t *drv, int *count)
279 {
280         const struct in_pdata *pdata = drv->pdata;
281         *count = SDLK_LAST;
282
283         if (pdata->key_names)
284                 return pdata->key_names;
285         return in_sdl_keys;
286 }
287
288 /* could use SDL_GetKeyState, but this gives better packing */
289 static void update_keystate(keybits_t *keystate, int sym, int is_down)
290 {
291         keybits_t *ks_word, mask;
292
293         mask = 1;
294         mask <<= sym & (KEYBITS_WORD_BITS - 1);
295         ks_word = keystate + sym / KEYBITS_WORD_BITS;
296         if (is_down)
297                 *ks_word |= mask;
298         else
299                 *ks_word &= ~mask;
300 }
301
302 static int get_keystate(keybits_t *keystate, int sym)
303 {
304         keybits_t *ks_word, mask;
305
306         mask = 1;
307         mask <<= sym & (KEYBITS_WORD_BITS - 1);
308         ks_word = keystate + sym / KEYBITS_WORD_BITS;
309         return !!(*ks_word & mask);
310 }
311
312 static int handle_event(struct in_sdl_state *state, SDL_Event *event,
313         int *kc_out, int *down_out, int *emu_out)
314 {
315         int emu;
316
317         if (event->type != SDL_KEYDOWN && event->type != SDL_KEYUP)
318                 return -1;
319
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);
323         if (kc_out != NULL)
324                 *kc_out = event->key.keysym.sym;
325         if (down_out != NULL)
326                 *down_out = event->type == SDL_KEYDOWN;
327         if (emu_out != NULL)
328                 *emu_out = emu;
329
330         return 1;
331 }
332
333 static int handle_joy_event(struct in_sdl_state *state, SDL_Event *event,
334         int *kc_out, int *down_out, int *emu_out)
335 {
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;
341
342         switch (event->type) {
343         case SDL_JOYAXISMOTION:
344                 if ((unsigned)event->jaxis.axis >= (unsigned)state->joy_numaxes)
345                         return 1;
346                 if (event->jaxis.which != state->joy_id)
347                         return -2;
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;
356                         down = 1;
357                 }
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;
361                         else
362                                 kc = kc_axis_base + event->jaxis.axis * 2 + 1;
363                         down = 1;
364                 }
365                 kc_prev = state->joy_axis_keydown[event->jaxis.axis];
366                 state->joy_axis_keydown[event->jaxis.axis] = kc;
367                 if (kc == kc_prev)
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;
372                         kc = kc_prev;
373                         down = 0;
374                 }
375                 else if (kc_prev >= 0)
376                         kc = kc_prev;
377                 ret = 1;
378                 break;
379
380         case SDL_JOYBUTTONDOWN:
381         case SDL_JOYBUTTONUP:
382                 if (event->jbutton.which != state->joy_id)
383                         return -2;
384                 kc = kc_button_base + (int)event->jbutton.button;
385                 down = event->jbutton.state == SDL_PRESSED;
386                 ret = 1;
387                 break;
388         case SDL_JOYHATMOTION:
389                 if (event->jhat.which != state->joy_id)
390                         return -2;
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) {
394                         if (xor & 1) {
395                                 if (event->jhat.hat)
396                                         kc = kc_hat_base + event->jhat.hat * 4 + i;
397                                 else
398                                         kc = hat2uldr[i];
399                                 down = val & 1;
400                                 ret = 1;
401                                 break;
402                         }
403                 }
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;
408                 }
409                 break;
410         default:
411                 //printf("joy ev %d\n", event->type);
412                 return -1;
413         }
414
415         if (ret) {
416                 emu |= get_keystate(state->emu_keys, kc);
417                 update_keystate(state->keystate, kc, down);
418         }
419         if (kc_out != NULL)
420                 *kc_out = kc;
421         if (down_out != NULL)
422                 *down_out = down;
423         if (emu_out != 0)
424                 *emu_out = emu;
425
426         return ret;
427 }
428
429 #define JOY_EVENTS (SDL_JOYAXISMOTIONMASK | SDL_JOYBALLMOTIONMASK | SDL_JOYHATMOTIONMASK \
430                     | SDL_JOYBUTTONDOWNMASK | SDL_JOYBUTTONUPMASK)
431
432 static int collect_events(struct in_sdl_state *state, int *one_kc, int *one_down)
433 {
434         SDL_Event events[8];
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;
438         SDL_Event *event;
439
440         SDL_PumpEvents();
441
442         maxcount = ARRAY_SIZE(events);
443         if ((count = SDL_PeepEvents(events, maxcount, SDL_GETEVENT, mask)) > 0) {
444                 for (i = 0; i < count; i++) {
445                         event = &events[i];
446                         if (state->joy) {
447                                 ret = handle_joy_event(state,
448                                         event, one_kc, one_down, &is_emukey);
449                         } else {
450                                 ret = handle_event(state,
451                                         event, one_kc, one_down, &is_emukey);
452                         }
453                         if (ret < 0) {
454                                 switch (ret) {
455                                         case -2:
456                                                 SDL_PushEvent(event);
457                                                 break;
458                                         default:
459                                                 if (event->type == SDL_VIDEORESIZE) {
460                                                         state->redraw = 1;
461                                                         state->revent = *event;
462                                                 } else if (event->type == SDL_VIDEOEXPOSE) {
463                                                         if (state->revent.type == SDL_NOEVENT) {
464                                                                 state->redraw = 1;
465                                                                 state->revent.type = SDL_VIDEOEXPOSE;
466                                                         }
467                                                 } else
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;
478                                                 }
479                                                 else if (ext_event_handler != NULL)
480                                                         ext_event_handler(event);
481                                                 break;
482                                 }
483                                 continue;
484                         }
485
486                         retval |= ret;
487                         if ((is_emukey || one_kc != NULL) && retval)
488                         {
489                                 break;
490                         }
491                 }
492         }
493
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);
498                 state->redraw = 0;
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;
504                         retval |= 1;
505                 }
506                 if (one_down != NULL)
507                         *one_down = 1;
508         } else
509                 i++;
510         // don't lose events other devices might want to handle
511         if (i < count)
512                 SDL_PeepEvents(events+i, count-i, SDL_ADDEVENT, mask);
513         return retval;
514 }
515
516 static int in_sdl_update(void *drv_data, const int *binds, int *result)
517 {
518         struct in_sdl_state *state = drv_data;
519         keybits_t mask;
520         int i, sym, bit, b;
521
522         collect_events(state, NULL, NULL);
523
524         for (i = 0; i < SDLK_LAST / KEYBITS_WORD_BITS + 1; i++) {
525                 mask = state->keystate[i];
526                 if (mask == 0)
527                         continue;
528                 for (bit = 0; mask != 0; bit++, mask >>= 1) {
529                         if ((mask & 1) == 0)
530                                 continue;
531                         sym = i * KEYBITS_WORD_BITS + bit;
532
533                         for (b = 0; b < IN_BINDTYPE_COUNT; b++)
534                                 result[b] |= binds[IN_BIND_OFFS(sym, b)];
535                 }
536         }
537
538         return 0;
539 }
540
541 static int in_sdl_update_kbd(void *drv_data, const int *binds, int *result)
542 {
543         struct in_sdl_state *state = drv_data;
544         keybits_t mask;
545         int i, sym, bit, b = 0;
546
547         collect_events(state, NULL, NULL);
548
549         for (i = 0; i < SDLK_LAST / KEYBITS_WORD_BITS + 1; i++) {
550                 mask = state->keystate[i];
551                 if (mask == 0)
552                         continue;
553                 for (bit = 0; mask != 0; bit++, mask >>= 1) {
554                         if ((mask & 1) == 0)
555                                 continue;
556                         sym = i * KEYBITS_WORD_BITS + bit;
557                         result[b++] = binds[sym];
558                 }
559         }
560
561         return b;
562 }
563
564 static int in_sdl_update_analog(void *drv_data, int axis_id, int *result)
565 {
566         struct in_sdl_state *state = drv_data;
567         int v;
568
569         if (!state || !state->joy || !result)
570                 return -1;
571         if ((unsigned)axis_id >= (unsigned)state->joy_numaxes)
572                 return -1;
573
574         v = SDL_JoystickGetAxis(state->joy, axis_id);
575
576         // -32768...32767 -> -IN_ABS_RANGE...IN_ABS_RANGE
577         *result = (v + ((v >> 31) | 1)) / (32768 / IN_ABS_RANGE);
578         return 0;
579 }
580
581 static int in_sdl_update_pointer(void *drv_data, int id, int *result)
582 {
583         struct in_sdl_state *state = drv_data;
584         int max;
585
586         *result = 0;
587
588         switch (id) {
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;
592                 break;
593         case 1: if ((max = state->revent.resize.h))
594                         *result = state->mevent.motion.y * 2*1024/max - 1024;
595                 break;
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;
600                 break;
601         case 3: if ((max = state->revent.resize.h))
602                         *result = state->mevent.motion.yrel * 2*1024/max;
603                 state->mevent.motion.yrel = 0;
604                 break;
605         // buttons
606         case -1: *result = state->mevent.motion.state;
607                 break;
608         default: return -1;
609         }
610
611         return 0;
612 }
613
614 static int in_sdl_update_keycode(void *drv_data, int *is_down)
615 {
616         struct in_sdl_state *state = drv_data;
617         int ret_kc = -1, ret_down = 0;
618
619         collect_events(state, &ret_kc, &ret_down);
620
621         if (is_down != NULL)
622                 *is_down = ret_down;
623
624         return ret_kc;
625 }
626
627 static int in_sdl_menu_translate(void *drv_data, int keycode, char *charcode)
628 {
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;
633         int map_len;
634         int ret = 0;
635         int i;
636
637         if (pdata->key_names)
638                 key_names = pdata->key_names;
639
640         if (state->joy) {
641                 map = pdata->joy_map;
642                 map_len = pdata->jmap_size;
643         } else {
644                 map = pdata->key_map;
645                 map_len = pdata->kmap_size;
646         }
647
648         if (keycode < 0)
649         {
650                 /* menu -> kc */
651                 keycode = -keycode;
652                 for (i = 0; i < map_len; i++)
653                         if (map[i].pbtn == keycode)
654                                 return map[i].key;
655         }
656         else
657         {
658                 if (keycode == SDLK_UNKNOWN)
659                         ret = PBTN_RDRAW;
660                 else
661                 for (i = 0; i < map_len; i++) {
662                         if (map[i].key == keycode) {
663                                 ret = map[i].pbtn;
664                                 break;
665                         }
666                 }
667
668                 if (charcode != NULL && (unsigned int)keycode < SDLK_LAST &&
669                     key_names[keycode] != NULL && key_names[keycode][1] == 0)
670                 {
671                         ret |= PBTN_CHAR;
672                         *charcode = key_names[keycode][0];
673                 }
674         }
675
676         return ret;
677 }
678
679 static int in_sdl_clean_binds(void *drv_data, int *binds, int *def_binds)
680 {
681         struct in_sdl_state *state = drv_data;
682         int i, t, cnt = 0;
683
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;
690                         if (binds[offs]) {
691                                 if (t == IN_BINDTYPE_EMU)
692                                         update_keystate(state->emu_keys, i, 1);
693                                 cnt ++;
694                         }
695                 }
696         }
697
698         return cnt;
699 }
700
701 static int in_sdl_get_config(void *drv_data, enum in_cfg_opt what, int *val)
702 {
703         struct in_sdl_state *state = drv_data;
704
705         switch (what) {
706         case IN_CFG_ABS_AXIS_COUNT:
707                 *val = state->joy_numaxes;
708                 break;
709         case IN_CFG_ANALOG_MAP_ULDR:
710                 *val = state->abs_to_udlr;
711                 break;
712         default:
713                 return -1;
714         }
715
716         return 0;
717 }
718
719 static int in_sdl_set_config(void *drv_data, enum in_cfg_opt what, int val)
720 {
721         struct in_sdl_state *state = drv_data;
722         size_t i;
723
724         switch (what) {
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)
729                                 continue;
730                         update_keystate(state->keystate, state->joy_axis_keydown[i], 0);
731                         state->joy_axis_keydown[i] = -1;
732                 }
733                 break;
734         default:
735                 return -1;
736         }
737
738         return 0;
739 }
740
741 static const in_drv_t in_sdl_drv = {
742         .prefix          = IN_SDL_PREFIX,
743         .probe           = in_sdl_probe,
744         .free            = in_sdl_free,
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,
755 };
756
757 int in_sdl_init(const struct in_pdata *pdata, void (*handler)(void *event))
758 {
759         if (!pdata) {
760                 fprintf(stderr, "in_sdl: Missing input platform data\n");
761                 return -1;
762         }
763
764         in_register_driver(&in_sdl_drv, pdata->defbinds, pdata->kbd_map, pdata);
765         ext_event_handler = handler;
766         return 0;
767 }