in_sdl: give names to gamepad buttons
[libpicofe.git] / in_sdl.c
CommitLineData
d1dc092f 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.
f89d8471 8 * - MAME license.
d1dc092f 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 */
19typedef unsigned long keybits_t;
20#define KEYBITS_WORD_BITS (sizeof(keybits_t) * 8)
21
e3c0b886 22#ifndef ARRAY_SIZE
23#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
24#endif
25
6407ad5b 26struct in_sdl_state {
c19e28f6 27 const in_drv_t *drv;
6407ad5b 28 SDL_Joystick *joy;
29 int joy_id;
ec6c16b1 30 int joy_numaxes;
31 int joy_numbuttons;
32 int *joy_axis_keydown;
33 int joy_hat_down;
f5e4164a 34 unsigned int joy_axis_as_btn; // bitmask; vs axes of centered sticks
e3c0b886 35 unsigned int redraw:1;
36 unsigned int abs_to_udlr:1;
37 unsigned int hat_warned:1;
92bd9c36 38 SDL_Event revent;
7bd23aaf 39 SDL_Event mevent; // last mouse event
6407ad5b 40 keybits_t keystate[SDLK_LAST / KEYBITS_WORD_BITS + 1];
271f9001 41 // emulator keys should always be processed immediately lest one is lost
42 keybits_t emu_keys[SDLK_LAST / KEYBITS_WORD_BITS + 1];
6407ad5b 43};
44
9227a777 45static void (*ext_event_handler)(void *event);
46
d1dc092f 47static 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] = ":",
d1f5849c 80 [SDLK_SEMICOLON] = ";",
d1dc092f 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
50d625e8 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
d1dc092f 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",
d1dc092f 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
ec6c16b1 190static struct in_sdl_state *state_alloc(int joy_numaxes)
e3c0b886 191{
192 struct in_sdl_state *state;
e3c0b886 193
194 state = calloc(1, sizeof(*state));
195 if (state == NULL) {
196 fprintf(stderr, "in_sdl: OOM\n");
197 return NULL;
198 }
199
ec6c16b1 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 }
e3c0b886 210 return state;
211}
212
c19e28f6 213static void in_sdl_probe(const in_drv_t *drv)
d1dc092f 214{
d685ce46
PC
215 const struct in_pdata *pdata = drv->pdata;
216 const char * const * key_names = in_sdl_keys;
6407ad5b 217 struct in_sdl_state *state;
218 SDL_Joystick *joy;
f5e4164a 219 int i, a, joycount;
6407ad5b 220 char name[256];
d1dc092f 221
d685ce46
PC
222 if (pdata->key_names)
223 key_names = pdata->key_names;
224
ec6c16b1 225 if (!(state = state_alloc(0)))
d1dc092f 226 return;
d1dc092f 227
c19e28f6 228 state->drv = drv;
6407ad5b 229 in_register(IN_SDL_PREFIX "keys", -1, state, SDLK_LAST,
d685ce46 230 key_names, 0);
e0d65e94 231 //SDL_EnableUNICODE(1);
6407ad5b 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
ec6c16b1 242 if (!(state = state_alloc(SDL_JoystickNumAxes(joy))))
6407ad5b 243 break;
6407ad5b 244 state->joy = joy;
245 state->joy_id = i;
ec6c16b1 246 state->joy_numbuttons = SDL_JoystickNumButtons(joy);
c19e28f6 247 state->drv = drv;
f5e4164a 248 for (a = 0; a < state->joy_numaxes; a++)
249 if (SDL_JoystickGetAxis(joy, a) < -16384)
250 state->joy_axis_as_btn |= 1u << a;
6407ad5b 251
252 snprintf(name, sizeof(name), IN_SDL_PREFIX "%s", SDL_JoystickName(i));
d685ce46 253 in_register(name, -1, state, SDLK_LAST, key_names, 0);
f5e4164a 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);
6407ad5b 259 }
260
261 if (joycount > 0)
262 SDL_JoystickEventState(SDL_ENABLE);
d1dc092f 263}
264
265static void in_sdl_free(void *drv_data)
266{
6407ad5b 267 struct in_sdl_state *state = drv_data;
d1dc092f 268
6407ad5b 269 if (state != NULL) {
270 if (state->joy != NULL)
271 SDL_JoystickClose(state->joy);
ec6c16b1 272 free(state->joy_axis_keydown);
6407ad5b 273 free(state);
274 }
d1dc092f 275}
276
277static const char * const *
d685ce46 278in_sdl_get_key_names(const in_drv_t *drv, int *count)
d1dc092f 279{
d685ce46 280 const struct in_pdata *pdata = drv->pdata;
d1dc092f 281 *count = SDLK_LAST;
d685ce46
PC
282
283 if (pdata->key_names)
284 return pdata->key_names;
d1dc092f 285 return in_sdl_keys;
286}
287
288/* could use SDL_GetKeyState, but this gives better packing */
289static 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
271f9001 302static 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
6407ad5b 312static int handle_event(struct in_sdl_state *state, SDL_Event *event,
271f9001 313 int *kc_out, int *down_out, int *emu_out)
d1dc092f 314{
271f9001 315 int emu;
316
6407ad5b 317 if (event->type != SDL_KEYDOWN && event->type != SDL_KEYUP)
9227a777 318 return -1;
d1dc092f 319
271f9001 320 emu = get_keystate(state->emu_keys, event->key.keysym.sym);
6407ad5b 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;
bededcb4 327 if (emu_out != NULL)
271f9001 328 *emu_out = emu;
6407ad5b 329
330 return 1;
331}
d1dc092f 332
6407ad5b 333static int handle_joy_event(struct in_sdl_state *state, SDL_Event *event,
271f9001 334 int *kc_out, int *down_out, int *emu_out)
6407ad5b 335{
ec6c16b1 336 static const int hat2uldr[4] = { SDLK_UP, SDLK_RIGHT, SDLK_DOWN, SDLK_LEFT };
e3c0b886 337 int kc = -1, kc_prev = -1, down = 0, emu = 0, ret = 0, i, val, xor;
ec6c16b1 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;
6407ad5b 341
6407ad5b 342 switch (event->type) {
343 case SDL_JOYAXISMOTION:
ec6c16b1 344 if ((unsigned)event->jaxis.axis >= (unsigned)state->joy_numaxes)
345 return 1;
18306c32
VL
346 if (event->jaxis.which != state->joy_id)
347 return -2;
e3c0b886 348 if (event->jaxis.value < -16384) {
ec6c16b1 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? :(
f5e4164a 354 else if (!(state->joy_axis_as_btn & (1u << event->jaxis.axis)))
355 kc = kc_axis_base + event->jaxis.axis * 2;
6407ad5b 356 down = 1;
6407ad5b 357 }
358 else if (event->jaxis.value > 16384) {
ec6c16b1 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;
6407ad5b 363 down = 1;
6407ad5b 364 }
ec6c16b1 365 kc_prev = state->joy_axis_keydown[event->jaxis.axis];
366 state->joy_axis_keydown[event->jaxis.axis] = kc;
e3c0b886 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
ec6c16b1 371 state->joy_axis_keydown[event->jaxis.axis] = -1;
e3c0b886 372 kc = kc_prev;
373 down = 0;
374 }
375 else if (kc_prev >= 0)
376 kc = kc_prev;
377 ret = 1;
6407ad5b 378 break;
379
380 case SDL_JOYBUTTONDOWN:
381 case SDL_JOYBUTTONUP:
18306c32
VL
382 if (event->jbutton.which != state->joy_id)
383 return -2;
ec6c16b1 384 kc = kc_button_base + (int)event->jbutton.button;
6407ad5b 385 down = event->jbutton.state == SDL_PRESSED;
386 ret = 1;
387 break;
e3c0b886 388 case SDL_JOYHATMOTION:
389 if (event->jhat.which != state->joy_id)
390 return -2;
ec6c16b1 391 xor = event->jhat.value ^ state->joy_hat_down;
392 val = state->joy_hat_down = event->jhat.value;
e3c0b886 393 for (i = 0; i < 4; i++, xor >>= 1, val >>= 1) {
394 if (xor & 1) {
ec6c16b1 395 if (event->jhat.hat)
396 kc = kc_hat_base + event->jhat.hat * 4 + i;
397 else
398 kc = hat2uldr[i];
e3c0b886 399 down = val & 1;
e3c0b886 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;
9227a777 410 default:
e3c0b886 411 //printf("joy ev %d\n", event->type);
9227a777 412 return -1;
d1dc092f 413 }
414
271f9001 415 if (ret) {
416 emu |= get_keystate(state->emu_keys, kc);
6407ad5b 417 update_keystate(state->keystate, kc, down);
271f9001 418 }
6407ad5b 419 if (kc_out != NULL)
420 *kc_out = kc;
421 if (down_out != NULL)
422 *down_out = down;
271f9001 423 if (emu_out != 0)
424 *emu_out = emu;
6407ad5b 425
426 return ret;
427}
428
429#define JOY_EVENTS (SDL_JOYAXISMOTIONMASK | SDL_JOYBALLMOTIONMASK | SDL_JOYHATMOTIONMASK \
430 | SDL_JOYBUTTONDOWNMASK | SDL_JOYBUTTONUPMASK)
431
432static int collect_events(struct in_sdl_state *state, int *one_kc, int *one_down)
433{
7cddc27a 434 SDL_Event events[8];
6407ad5b 435 Uint32 mask = state->joy ? JOY_EVENTS : (SDL_ALLEVENTS & ~JOY_EVENTS);
bededcb4 436 int count, maxcount, is_emukey = 0;
7cddc27a 437 int i = 0, ret = 0, retval = 0;
18306c32 438 SDL_Event *event;
6407ad5b 439
6407ad5b 440 SDL_PumpEvents();
18306c32 441
e3c0b886 442 maxcount = ARRAY_SIZE(events);
7cddc27a 443 if ((count = SDL_PeepEvents(events, maxcount, SDL_GETEVENT, mask)) > 0) {
6407ad5b 444 for (i = 0; i < count; i++) {
18306c32 445 event = &events[i];
271f9001 446 if (state->joy) {
6407ad5b 447 ret = handle_joy_event(state,
271f9001 448 event, one_kc, one_down, &is_emukey);
449 } else {
6407ad5b 450 ret = handle_event(state,
271f9001 451 event, one_kc, one_down, &is_emukey);
452 }
18306c32
VL
453 if (ret < 0) {
454 switch (ret) {
455 case -2:
456 SDL_PushEvent(event);
457 break;
458 default:
f0615849 459 if (event->type == SDL_VIDEORESIZE) {
92bd9c36 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;
7bd23aaf 465 state->revent.type = SDL_VIDEOEXPOSE;
92bd9c36 466 }
f0615849 467 } else
7bd23aaf 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;
ce684c88 474 } else if (event->type == SDL_MOUSEMOTION) {
475 event->motion.xrel += state->mevent.motion.xrel;
476 event->motion.yrel += state->mevent.motion.yrel;
7bd23aaf 477 state->mevent = *event;
ce684c88 478 }
7bd23aaf 479 else if (ext_event_handler != NULL)
18306c32
VL
480 ext_event_handler(event);
481 break;
482 }
7cddc27a 483 continue;
9227a777 484 }
485
6407ad5b 486 retval |= ret;
bededcb4 487 if ((is_emukey || one_kc != NULL) && retval)
18306c32 488 {
7cddc27a 489 break;
18306c32 490 }
6407ad5b 491 }
492 }
493
92bd9c36 494 // if the event queue has been emptied and resize/expose events were in it
495 if (state->redraw && count == 0) {
f0615849 496 if (ext_event_handler != NULL)
92bd9c36 497 ext_event_handler(&state->revent);
498 state->redraw = 0;
499 state->revent.type = SDL_NOEVENT;
7cddc27a 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
c825b167 509 i++;
7cddc27a 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);
6407ad5b 513 return retval;
514}
515
516static 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
d1dc092f 524 for (i = 0; i < SDLK_LAST / KEYBITS_WORD_BITS + 1; i++) {
6407ad5b 525 mask = state->keystate[i];
d1dc092f 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
4acbd7fe 541static int in_sdl_update_kbd(void *drv_data, const int *binds, int *result)
b047d2e2 542{
543 struct in_sdl_state *state = drv_data;
544 keybits_t mask;
842f0c7c 545 int i, sym, bit, b = 0;
b047d2e2 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;
842f0c7c 557 result[b++] = binds[sym];
b047d2e2 558 }
559 }
560
842f0c7c 561 return b;
b047d2e2 562}
563
9874363e 564static 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;
ec6c16b1 571 if ((unsigned)axis_id >= (unsigned)state->joy_numaxes)
9874363e 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
b020891a 581static int in_sdl_update_pointer(void *drv_data, int id, int *result)
7bd23aaf 582{
583 struct in_sdl_state *state = drv_data;
584 int max;
585
7bd23aaf 586 *result = 0;
ce684c88 587
b020891a 588 switch (id) {
ce684c88 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 }
7bd23aaf 610
611 return 0;
612}
b047d2e2 613
d1dc092f 614static int in_sdl_update_keycode(void *drv_data, int *is_down)
615{
6407ad5b 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
d1dc092f 621 if (is_down != NULL)
622 *is_down = ret_down;
623
624 return ret_kc;
625}
626
d1dc092f 627static int in_sdl_menu_translate(void *drv_data, int keycode, char *charcode)
628{
6407ad5b 629 struct in_sdl_state *state = drv_data;
c19e28f6 630 const struct in_pdata *pdata = state->drv->pdata;
d685ce46 631 const char * const * key_names = in_sdl_keys;
6407ad5b 632 const struct menu_keymap *map;
633 int map_len;
d1dc092f 634 int ret = 0;
635 int i;
636
d685ce46
PC
637 if (pdata->key_names)
638 key_names = pdata->key_names;
639
c19e28f6
PC
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 }
6407ad5b 647
d1dc092f 648 if (keycode < 0)
649 {
650 /* menu -> kc */
651 keycode = -keycode;
6407ad5b 652 for (i = 0; i < map_len; i++)
653 if (map[i].pbtn == keycode)
654 return map[i].key;
d1dc092f 655 }
656 else
657 {
bededcb4 658 if (keycode == SDLK_UNKNOWN)
659 ret = PBTN_RDRAW;
660 else
6407ad5b 661 for (i = 0; i < map_len; i++) {
662 if (map[i].key == keycode) {
663 ret = map[i].pbtn;
d1dc092f 664 break;
665 }
666 }
667
668 if (charcode != NULL && (unsigned int)keycode < SDLK_LAST &&
d685ce46 669 key_names[keycode] != NULL && key_names[keycode][1] == 0)
d1dc092f 670 {
671 ret |= PBTN_CHAR;
d685ce46 672 *charcode = key_names[keycode][0];
d1dc092f 673 }
674 }
675
676 return ret;
677}
678
5d8d9079 679static int in_sdl_clean_binds(void *drv_data, int *binds, int *def_binds)
271f9001 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));
5d8d9079 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]) {
271f9001 691 if (t == IN_BINDTYPE_EMU)
692 update_keystate(state->emu_keys, i, 1);
693 cnt ++;
694 }
5d8d9079 695 }
696 }
271f9001 697
698 return cnt;
699}
700
3f4a964c 701static int in_sdl_get_config(void *drv_data, enum in_cfg_opt what, int *val)
9874363e 702{
703 struct in_sdl_state *state = drv_data;
704
705 switch (what) {
706 case IN_CFG_ABS_AXIS_COUNT:
ec6c16b1 707 *val = state->joy_numaxes;
9874363e 708 break;
e3c0b886 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
719static 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;
ec6c16b1 727 for (i = 0; !val && i < state->joy_numaxes; i++) {
728 if (state->joy_axis_keydown[i] < 0)
e3c0b886 729 continue;
ec6c16b1 730 update_keystate(state->keystate, state->joy_axis_keydown[i], 0);
731 state->joy_axis_keydown[i] = -1;
e3c0b886 732 }
733 break;
9874363e 734 default:
735 return -1;
736 }
737
738 return 0;
739}
740
d1dc092f 741static const in_drv_t in_sdl_drv = {
b047d2e2 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,
4acbd7fe 747 .update_kbd = in_sdl_update_kbd,
9874363e 748 .update_analog = in_sdl_update_analog,
b020891a 749 .update_pointer = in_sdl_update_pointer,
b047d2e2 750 .update_keycode = in_sdl_update_keycode,
751 .menu_translate = in_sdl_menu_translate,
752 .clean_binds = in_sdl_clean_binds,
9874363e 753 .get_config = in_sdl_get_config,
e3c0b886 754 .set_config = in_sdl_set_config,
d1dc092f 755};
756
c19e28f6 757int in_sdl_init(const struct in_pdata *pdata, void (*handler)(void *event))
d1dc092f 758{
c19e28f6
PC
759 if (!pdata) {
760 fprintf(stderr, "in_sdl: Missing input platform data\n");
761 return -1;
762 }
763
4acbd7fe 764 in_register_driver(&in_sdl_drv, pdata->defbinds, pdata->kbd_map, pdata);
9227a777 765 ext_event_handler = handler;
c19e28f6 766 return 0;
d1dc092f 767}