SDL_Libretro
SDL3-power libretro frontend.
Loading...
Searching...
No Matches
SDL_libretro_input.h
Go to the documentation of this file.
1
6#if defined(SDL_LIBRETRO_IMPLEMENTATION) && !defined(SDL_LIBRETRO_INPUT_IMPL_ONCE)
7#define SDL_LIBRETRO_INPUT_IMPL_ONCE
8
15static SDL_GamepadButton SDL_Libretro_RetroJoypadToGamepadButton(unsigned button) {
16 switch (button) {
17 case RETRO_DEVICE_ID_JOYPAD_B: return SDL_GAMEPAD_BUTTON_SOUTH;
18 case RETRO_DEVICE_ID_JOYPAD_Y: return SDL_GAMEPAD_BUTTON_WEST;
19 case RETRO_DEVICE_ID_JOYPAD_SELECT: return SDL_GAMEPAD_BUTTON_BACK;
20 case RETRO_DEVICE_ID_JOYPAD_START: return SDL_GAMEPAD_BUTTON_START;
21 case RETRO_DEVICE_ID_JOYPAD_UP: return SDL_GAMEPAD_BUTTON_DPAD_UP;
22 case RETRO_DEVICE_ID_JOYPAD_DOWN: return SDL_GAMEPAD_BUTTON_DPAD_DOWN;
23 case RETRO_DEVICE_ID_JOYPAD_LEFT: return SDL_GAMEPAD_BUTTON_DPAD_LEFT;
24 case RETRO_DEVICE_ID_JOYPAD_RIGHT: return SDL_GAMEPAD_BUTTON_DPAD_RIGHT;
25 case RETRO_DEVICE_ID_JOYPAD_A: return SDL_GAMEPAD_BUTTON_EAST;
26 case RETRO_DEVICE_ID_JOYPAD_X: return SDL_GAMEPAD_BUTTON_NORTH;
27 case RETRO_DEVICE_ID_JOYPAD_L: return SDL_GAMEPAD_BUTTON_LEFT_SHOULDER;
28 case RETRO_DEVICE_ID_JOYPAD_R: return SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER;
29 case RETRO_DEVICE_ID_JOYPAD_L2: return SDL_GAMEPAD_BUTTON_LEFT_PADDLE1;
30 case RETRO_DEVICE_ID_JOYPAD_R2: return SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1;
31 case RETRO_DEVICE_ID_JOYPAD_L3: return SDL_GAMEPAD_BUTTON_LEFT_STICK;
32 case RETRO_DEVICE_ID_JOYPAD_R3: return SDL_GAMEPAD_BUTTON_RIGHT_STICK;
33 default: return SDL_GAMEPAD_BUTTON_INVALID;
34 }
35}
36
44static SDL_Scancode SDL_Libretro_RetroKeyToScancode(unsigned key) {
45 switch (key) {
46 case RETROK_BACKSPACE: return SDL_SCANCODE_BACKSPACE;
47 case RETROK_TAB: return SDL_SCANCODE_TAB;
48 case RETROK_RETURN: return SDL_SCANCODE_RETURN;
49 case RETROK_ESCAPE: return SDL_SCANCODE_ESCAPE;
50 case RETROK_SPACE: return SDL_SCANCODE_SPACE;
51 case RETROK_EXCLAIM: return SDL_SCANCODE_1;
52 case RETROK_QUOTEDBL: return SDL_SCANCODE_APOSTROPHE;
53 case RETROK_HASH: return SDL_SCANCODE_3;
54 case RETROK_DOLLAR: return SDL_SCANCODE_4;
55 case RETROK_AMPERSAND: return SDL_SCANCODE_7;
56 case RETROK_QUOTE: return SDL_SCANCODE_APOSTROPHE;
57 case RETROK_LEFTPAREN: return SDL_SCANCODE_9;
58 case RETROK_RIGHTPAREN: return SDL_SCANCODE_0;
59 case RETROK_ASTERISK: return SDL_SCANCODE_8;
60 case RETROK_PLUS: return SDL_SCANCODE_EQUALS;
61 case RETROK_COMMA: return SDL_SCANCODE_COMMA;
62 case RETROK_MINUS: return SDL_SCANCODE_MINUS;
63 case RETROK_PERIOD: return SDL_SCANCODE_PERIOD;
64 case RETROK_SLASH: return SDL_SCANCODE_SLASH;
65 case RETROK_0: return SDL_SCANCODE_0;
66 case RETROK_1: return SDL_SCANCODE_1;
67 case RETROK_2: return SDL_SCANCODE_2;
68 case RETROK_3: return SDL_SCANCODE_3;
69 case RETROK_4: return SDL_SCANCODE_4;
70 case RETROK_5: return SDL_SCANCODE_5;
71 case RETROK_6: return SDL_SCANCODE_6;
72 case RETROK_7: return SDL_SCANCODE_7;
73 case RETROK_8: return SDL_SCANCODE_8;
74 case RETROK_9: return SDL_SCANCODE_9;
75 case RETROK_SEMICOLON: return SDL_SCANCODE_SEMICOLON;
76 case RETROK_EQUALS: return SDL_SCANCODE_EQUALS;
77 case RETROK_LEFTBRACKET: return SDL_SCANCODE_LEFTBRACKET;
78 case RETROK_BACKSLASH: return SDL_SCANCODE_BACKSLASH;
79 case RETROK_RIGHTBRACKET: return SDL_SCANCODE_RIGHTBRACKET;
80 case RETROK_BACKQUOTE: return SDL_SCANCODE_GRAVE;
81 case RETROK_a: return SDL_SCANCODE_A;
82 case RETROK_b: return SDL_SCANCODE_B;
83 case RETROK_c: return SDL_SCANCODE_C;
84 case RETROK_d: return SDL_SCANCODE_D;
85 case RETROK_e: return SDL_SCANCODE_E;
86 case RETROK_f: return SDL_SCANCODE_F;
87 case RETROK_g: return SDL_SCANCODE_G;
88 case RETROK_h: return SDL_SCANCODE_H;
89 case RETROK_i: return SDL_SCANCODE_I;
90 case RETROK_j: return SDL_SCANCODE_J;
91 case RETROK_k: return SDL_SCANCODE_K;
92 case RETROK_l: return SDL_SCANCODE_L;
93 case RETROK_m: return SDL_SCANCODE_M;
94 case RETROK_n: return SDL_SCANCODE_N;
95 case RETROK_o: return SDL_SCANCODE_O;
96 case RETROK_p: return SDL_SCANCODE_P;
97 case RETROK_q: return SDL_SCANCODE_Q;
98 case RETROK_r: return SDL_SCANCODE_R;
99 case RETROK_s: return SDL_SCANCODE_S;
100 case RETROK_t: return SDL_SCANCODE_T;
101 case RETROK_u: return SDL_SCANCODE_U;
102 case RETROK_v: return SDL_SCANCODE_V;
103 case RETROK_w: return SDL_SCANCODE_W;
104 case RETROK_x: return SDL_SCANCODE_X;
105 case RETROK_y: return SDL_SCANCODE_Y;
106 case RETROK_z: return SDL_SCANCODE_Z;
107 case RETROK_DELETE: return SDL_SCANCODE_DELETE;
108 case RETROK_KP0: return SDL_SCANCODE_KP_0;
109 case RETROK_KP1: return SDL_SCANCODE_KP_1;
110 case RETROK_KP2: return SDL_SCANCODE_KP_2;
111 case RETROK_KP3: return SDL_SCANCODE_KP_3;
112 case RETROK_KP4: return SDL_SCANCODE_KP_4;
113 case RETROK_KP5: return SDL_SCANCODE_KP_5;
114 case RETROK_KP6: return SDL_SCANCODE_KP_6;
115 case RETROK_KP7: return SDL_SCANCODE_KP_7;
116 case RETROK_KP8: return SDL_SCANCODE_KP_8;
117 case RETROK_KP9: return SDL_SCANCODE_KP_9;
118 case RETROK_KP_PERIOD: return SDL_SCANCODE_KP_PERIOD;
119 case RETROK_KP_DIVIDE: return SDL_SCANCODE_KP_DIVIDE;
120 case RETROK_KP_MULTIPLY: return SDL_SCANCODE_KP_MULTIPLY;
121 case RETROK_KP_MINUS: return SDL_SCANCODE_KP_MINUS;
122 case RETROK_KP_PLUS: return SDL_SCANCODE_KP_PLUS;
123 case RETROK_KP_ENTER: return SDL_SCANCODE_KP_ENTER;
124 case RETROK_KP_EQUALS: return SDL_SCANCODE_KP_EQUALS;
125 case RETROK_UP: return SDL_SCANCODE_UP;
126 case RETROK_DOWN: return SDL_SCANCODE_DOWN;
127 case RETROK_RIGHT: return SDL_SCANCODE_RIGHT;
128 case RETROK_LEFT: return SDL_SCANCODE_LEFT;
129 case RETROK_INSERT: return SDL_SCANCODE_INSERT;
130 case RETROK_HOME: return SDL_SCANCODE_HOME;
131 case RETROK_END: return SDL_SCANCODE_END;
132 case RETROK_PAGEUP: return SDL_SCANCODE_PAGEUP;
133 case RETROK_PAGEDOWN: return SDL_SCANCODE_PAGEDOWN;
134 case RETROK_F1: return SDL_SCANCODE_F1;
135 case RETROK_F2: return SDL_SCANCODE_F2;
136 case RETROK_F3: return SDL_SCANCODE_F3;
137 case RETROK_F4: return SDL_SCANCODE_F4;
138 case RETROK_F5: return SDL_SCANCODE_F5;
139 case RETROK_F6: return SDL_SCANCODE_F6;
140 case RETROK_F7: return SDL_SCANCODE_F7;
141 case RETROK_F8: return SDL_SCANCODE_F8;
142 case RETROK_F9: return SDL_SCANCODE_F9;
143 case RETROK_F10: return SDL_SCANCODE_F10;
144 case RETROK_F11: return SDL_SCANCODE_F11;
145 case RETROK_F12: return SDL_SCANCODE_F12;
146 case RETROK_F13: return SDL_SCANCODE_F13;
147 case RETROK_F14: return SDL_SCANCODE_F14;
148 case RETROK_F15: return SDL_SCANCODE_F15;
149 case RETROK_NUMLOCK: return SDL_SCANCODE_NUMLOCKCLEAR;
150 case RETROK_CAPSLOCK: return SDL_SCANCODE_CAPSLOCK;
151 case RETROK_SCROLLOCK: return SDL_SCANCODE_SCROLLLOCK;
152 case RETROK_RSHIFT: return SDL_SCANCODE_RSHIFT;
153 case RETROK_LSHIFT: return SDL_SCANCODE_LSHIFT;
154 case RETROK_RCTRL: return SDL_SCANCODE_RCTRL;
155 case RETROK_LCTRL: return SDL_SCANCODE_LCTRL;
156 case RETROK_RALT: return SDL_SCANCODE_RALT;
157 case RETROK_LALT: return SDL_SCANCODE_LALT;
158 case RETROK_RMETA: return SDL_SCANCODE_RGUI;
159 case RETROK_LMETA: return SDL_SCANCODE_LGUI;
160 case RETROK_RSUPER: return SDL_SCANCODE_RGUI;
161 case RETROK_LSUPER: return SDL_SCANCODE_LGUI;
162 case RETROK_MENU: return SDL_SCANCODE_MENU;
163 default: return SDL_SCANCODE_UNKNOWN;
164 }
165}
166
174static unsigned SDL_Libretro_ScancodeToRetroKey(SDL_Scancode scancode) {
175 switch (scancode) {
176 case SDL_SCANCODE_BACKSPACE: return RETROK_BACKSPACE;
177 case SDL_SCANCODE_TAB: return RETROK_TAB;
178 case SDL_SCANCODE_RETURN: return RETROK_RETURN;
179 case SDL_SCANCODE_ESCAPE: return RETROK_ESCAPE;
180 case SDL_SCANCODE_SPACE: return RETROK_SPACE;
181 case SDL_SCANCODE_APOSTROPHE: return RETROK_QUOTE;
182 case SDL_SCANCODE_COMMA: return RETROK_COMMA;
183 case SDL_SCANCODE_MINUS: return RETROK_MINUS;
184 case SDL_SCANCODE_PERIOD: return RETROK_PERIOD;
185 case SDL_SCANCODE_SLASH: return RETROK_SLASH;
186 case SDL_SCANCODE_0: return RETROK_0;
187 case SDL_SCANCODE_1: return RETROK_1;
188 case SDL_SCANCODE_2: return RETROK_2;
189 case SDL_SCANCODE_3: return RETROK_3;
190 case SDL_SCANCODE_4: return RETROK_4;
191 case SDL_SCANCODE_5: return RETROK_5;
192 case SDL_SCANCODE_6: return RETROK_6;
193 case SDL_SCANCODE_7: return RETROK_7;
194 case SDL_SCANCODE_8: return RETROK_8;
195 case SDL_SCANCODE_9: return RETROK_9;
196 case SDL_SCANCODE_SEMICOLON: return RETROK_SEMICOLON;
197 case SDL_SCANCODE_EQUALS: return RETROK_EQUALS;
198 case SDL_SCANCODE_LEFTBRACKET: return RETROK_LEFTBRACKET;
199 case SDL_SCANCODE_BACKSLASH: return RETROK_BACKSLASH;
200 case SDL_SCANCODE_RIGHTBRACKET: return RETROK_RIGHTBRACKET;
201 case SDL_SCANCODE_GRAVE: return RETROK_BACKQUOTE;
202 case SDL_SCANCODE_A: return RETROK_a;
203 case SDL_SCANCODE_B: return RETROK_b;
204 case SDL_SCANCODE_C: return RETROK_c;
205 case SDL_SCANCODE_D: return RETROK_d;
206 case SDL_SCANCODE_E: return RETROK_e;
207 case SDL_SCANCODE_F: return RETROK_f;
208 case SDL_SCANCODE_G: return RETROK_g;
209 case SDL_SCANCODE_H: return RETROK_h;
210 case SDL_SCANCODE_I: return RETROK_i;
211 case SDL_SCANCODE_J: return RETROK_j;
212 case SDL_SCANCODE_K: return RETROK_k;
213 case SDL_SCANCODE_L: return RETROK_l;
214 case SDL_SCANCODE_M: return RETROK_m;
215 case SDL_SCANCODE_N: return RETROK_n;
216 case SDL_SCANCODE_O: return RETROK_o;
217 case SDL_SCANCODE_P: return RETROK_p;
218 case SDL_SCANCODE_Q: return RETROK_q;
219 case SDL_SCANCODE_R: return RETROK_r;
220 case SDL_SCANCODE_S: return RETROK_s;
221 case SDL_SCANCODE_T: return RETROK_t;
222 case SDL_SCANCODE_U: return RETROK_u;
223 case SDL_SCANCODE_V: return RETROK_v;
224 case SDL_SCANCODE_W: return RETROK_w;
225 case SDL_SCANCODE_X: return RETROK_x;
226 case SDL_SCANCODE_Y: return RETROK_y;
227 case SDL_SCANCODE_Z: return RETROK_z;
228 case SDL_SCANCODE_DELETE: return RETROK_DELETE;
229 case SDL_SCANCODE_KP_0: return RETROK_KP0;
230 case SDL_SCANCODE_KP_1: return RETROK_KP1;
231 case SDL_SCANCODE_KP_2: return RETROK_KP2;
232 case SDL_SCANCODE_KP_3: return RETROK_KP3;
233 case SDL_SCANCODE_KP_4: return RETROK_KP4;
234 case SDL_SCANCODE_KP_5: return RETROK_KP5;
235 case SDL_SCANCODE_KP_6: return RETROK_KP6;
236 case SDL_SCANCODE_KP_7: return RETROK_KP7;
237 case SDL_SCANCODE_KP_8: return RETROK_KP8;
238 case SDL_SCANCODE_KP_9: return RETROK_KP9;
239 case SDL_SCANCODE_KP_PERIOD: return RETROK_KP_PERIOD;
240 case SDL_SCANCODE_KP_DIVIDE: return RETROK_KP_DIVIDE;
241 case SDL_SCANCODE_KP_MULTIPLY: return RETROK_KP_MULTIPLY;
242 case SDL_SCANCODE_KP_MINUS: return RETROK_KP_MINUS;
243 case SDL_SCANCODE_KP_PLUS: return RETROK_KP_PLUS;
244 case SDL_SCANCODE_KP_ENTER: return RETROK_KP_ENTER;
245 case SDL_SCANCODE_KP_EQUALS: return RETROK_KP_EQUALS;
246 case SDL_SCANCODE_UP: return RETROK_UP;
247 case SDL_SCANCODE_DOWN: return RETROK_DOWN;
248 case SDL_SCANCODE_RIGHT: return RETROK_RIGHT;
249 case SDL_SCANCODE_LEFT: return RETROK_LEFT;
250 case SDL_SCANCODE_INSERT: return RETROK_INSERT;
251 case SDL_SCANCODE_HOME: return RETROK_HOME;
252 case SDL_SCANCODE_END: return RETROK_END;
253 case SDL_SCANCODE_PAGEUP: return RETROK_PAGEUP;
254 case SDL_SCANCODE_PAGEDOWN: return RETROK_PAGEDOWN;
255 case SDL_SCANCODE_F1: return RETROK_F1;
256 case SDL_SCANCODE_F2: return RETROK_F2;
257 case SDL_SCANCODE_F3: return RETROK_F3;
258 case SDL_SCANCODE_F4: return RETROK_F4;
259 case SDL_SCANCODE_F5: return RETROK_F5;
260 case SDL_SCANCODE_F6: return RETROK_F6;
261 case SDL_SCANCODE_F7: return RETROK_F7;
262 case SDL_SCANCODE_F8: return RETROK_F8;
263 case SDL_SCANCODE_F9: return RETROK_F9;
264 case SDL_SCANCODE_F10: return RETROK_F10;
265 case SDL_SCANCODE_F11: return RETROK_F11;
266 case SDL_SCANCODE_F12: return RETROK_F12;
267 case SDL_SCANCODE_F13: return RETROK_F13;
268 case SDL_SCANCODE_F14: return RETROK_F14;
269 case SDL_SCANCODE_F15: return RETROK_F15;
270 case SDL_SCANCODE_NUMLOCKCLEAR: return RETROK_NUMLOCK;
271 case SDL_SCANCODE_CAPSLOCK: return RETROK_CAPSLOCK;
272 case SDL_SCANCODE_SCROLLLOCK: return RETROK_SCROLLOCK;
273 case SDL_SCANCODE_RSHIFT: return RETROK_RSHIFT;
274 case SDL_SCANCODE_LSHIFT: return RETROK_LSHIFT;
275 case SDL_SCANCODE_RCTRL: return RETROK_RCTRL;
276 case SDL_SCANCODE_LCTRL: return RETROK_LCTRL;
277 case SDL_SCANCODE_RALT: return RETROK_RALT;
278 case SDL_SCANCODE_LALT: return RETROK_LALT;
279 case SDL_SCANCODE_RGUI: return RETROK_RMETA;
280 case SDL_SCANCODE_LGUI: return RETROK_LMETA;
281 case SDL_SCANCODE_MENU: return RETROK_MENU;
282 default: return RETROK_UNKNOWN;
283 }
284}
285
291static uint16_t SDL_Libretro_KeymodToRetroMod(SDL_Keymod mod) {
292 uint16_t retromod = RETROKMOD_NONE;
293 if (mod & SDL_KMOD_SHIFT) retromod |= RETROKMOD_SHIFT;
294 if (mod & SDL_KMOD_CTRL) retromod |= RETROKMOD_CTRL;
295 if (mod & SDL_KMOD_ALT) retromod |= RETROKMOD_ALT;
296 if (mod & SDL_KMOD_GUI) retromod |= RETROKMOD_META;
297 if (mod & SDL_KMOD_NUM) retromod |= RETROKMOD_NUMLOCK;
298 if (mod & SDL_KMOD_CAPS) retromod |= RETROKMOD_CAPSLOCK;
299 if (mod & SDL_KMOD_SCROLL) retromod |= RETROKMOD_SCROLLOCK;
300 return retromod;
301}
302
303static void SDL_Libretro_InputPoll(void) {
304 SDL_Libretro* lr = SDL_Libretro_active;
305 if (!lr) return;
306
307 lr->core.inputLastMouseX = lr->core.inputMouseX;
308 lr->core.inputLastMouseY = lr->core.inputMouseY;
309 SDL_GetMouseState(&lr->core.inputMouseX, &lr->core.inputMouseY);
310}
311
312static bool SDL_Libretro_IsKeyDown(SDL_Libretro* lr, SDL_Scancode scancode) {
313 if (scancode == SDL_SCANCODE_UNKNOWN) return false;
314 const bool* state = SDL_GetKeyboardState(NULL);
315 return state[scancode];
316}
317
318static int16_t SDL_Libretro_InputState(unsigned port, unsigned device, unsigned index, unsigned id) {
319 SDL_Libretro* lr = SDL_Libretro_active;
320 if (!lr) return 0;
321
322 // During a backward step's throwaway re-run, report neutral input so the
323 // discarded frame doesn't react to currently-held buttons.
324 if (lr->rewindActive) return 0;
325
326 unsigned baseDevice = device & RETRO_DEVICE_MASK;
327
328 switch (baseDevice) {
329 case RETRO_DEVICE_JOYPAD: {
330 if (id == RETRO_DEVICE_ID_JOYPAD_MASK) {
331 int16_t mask = 0;
332 for (unsigned b = 0; b <= RETRO_DEVICE_ID_JOYPAD_R3; b++) {
333 if (SDL_Libretro_InputState(port, device, index, b)) {
334 mask |= (1 << b);
335 }
336 }
337 return mask;
338 }
339
340 /* Virtual joypad (port 0 only) */
341 if (port == 0 && id < 16 && lr->core.virtualJoypadState[id]) {
342 return 1;
343 }
344
345 /* Keyboard mapping (port 0) */
346 if (port == 0 && id <= RETRO_DEVICE_ID_JOYPAD_R3) {
347 if (SDL_Libretro_IsKeyDown(lr, lr->keyboardPlayer1[id])) {
348 return 1;
349 }
350 }
351
352 /* Gamepad */
353 if (port < 16 && lr->gamepads[port]) {
354 /* L2/R2 are axes, not buttons */
355 if (id == RETRO_DEVICE_ID_JOYPAD_L2) {
356 Sint16 val = SDL_GetGamepadAxis(lr->gamepads[port], SDL_GAMEPAD_AXIS_LEFT_TRIGGER);
357 return val > 8192 ? 1 : 0;
358 }
359 if (id == RETRO_DEVICE_ID_JOYPAD_R2) {
360 Sint16 val = SDL_GetGamepadAxis(lr->gamepads[port], SDL_GAMEPAD_AXIS_RIGHT_TRIGGER);
361 return val > 8192 ? 1 : 0;
362 }
363
364 SDL_GamepadButton btn = SDL_Libretro_RetroJoypadToGamepadButton(id);
365 if (btn != SDL_GAMEPAD_BUTTON_INVALID) {
366 return SDL_GetGamepadButton(lr->gamepads[port], btn) ? 1 : 0;
367 }
368 }
369 return 0;
370 }
371
372 case RETRO_DEVICE_ANALOG: {
373 if (port < 16 && lr->gamepads[port]) {
374 SDL_GamepadAxis axis = SDL_GAMEPAD_AXIS_INVALID;
375 if (index == RETRO_DEVICE_INDEX_ANALOG_LEFT) {
376 axis = (id == RETRO_DEVICE_ID_ANALOG_X) ? SDL_GAMEPAD_AXIS_LEFTX : SDL_GAMEPAD_AXIS_LEFTY;
377 } else if (index == RETRO_DEVICE_INDEX_ANALOG_RIGHT) {
378 axis = (id == RETRO_DEVICE_ID_ANALOG_X) ? SDL_GAMEPAD_AXIS_RIGHTX : SDL_GAMEPAD_AXIS_RIGHTY;
379 }
380 if (axis != SDL_GAMEPAD_AXIS_INVALID) {
381 return SDL_GetGamepadAxis(lr->gamepads[port], axis);
382 }
383 }
384 return 0;
385 }
386
387 case RETRO_DEVICE_KEYBOARD: {
388 SDL_Scancode sc = SDL_Libretro_RetroKeyToScancode(id);
389 return SDL_Libretro_IsKeyDown(lr, sc) ? 1 : 0;
390 }
391
392 case RETRO_DEVICE_MOUSE: {
393 switch (id) {
394 case RETRO_DEVICE_ID_MOUSE_X:
395 return (int16_t)(lr->core.inputMouseX - lr->core.inputLastMouseX);
396 case RETRO_DEVICE_ID_MOUSE_Y:
397 return (int16_t)(lr->core.inputMouseY - lr->core.inputLastMouseY);
398 case RETRO_DEVICE_ID_MOUSE_LEFT: {
399 Uint32 state = SDL_GetMouseState(NULL, NULL);
400 return (state & SDL_BUTTON_LMASK) ? 1 : 0;
401 }
402 case RETRO_DEVICE_ID_MOUSE_RIGHT: {
403 Uint32 state = SDL_GetMouseState(NULL, NULL);
404 return (state & SDL_BUTTON_RMASK) ? 1 : 0;
405 }
406 case RETRO_DEVICE_ID_MOUSE_MIDDLE: {
407 Uint32 state = SDL_GetMouseState(NULL, NULL);
408 return (state & SDL_BUTTON_MMASK) ? 1 : 0;
409 }
410 }
411 return 0;
412 }
413
414 case RETRO_DEVICE_POINTER: {
415 if (index > 0) return 0;
416
417 float mx = lr->core.inputMouseX;
418 float my = lr->core.inputMouseY;
419 SDL_FRect r = lr->core.renderDstRect;
420 bool inside = r.w > 0 && r.h > 0 &&
421 mx >= r.x && mx < r.x + r.w &&
422 my >= r.y && my < r.y + r.h;
423
424 switch (id) {
425 case RETRO_DEVICE_ID_POINTER_X:
426 case RETRO_DEVICE_ID_POINTER_Y: {
427 if (r.w <= 0.0f || r.h <= 0.0f) return 0;
428
429 // Position within the on-screen (post-rotation) image, 0..1.
430 float sx = (mx - r.x) / r.w;
431 float sy = (my - r.y) / r.h;
432
433 // Undo the display rotation applied by SDL_Libretro_Render()
434 // (counter-clockwise, per libretro SET_ROTATION) to recover the
435 // core's unrotated framebuffer coordinates.
436 float u, v;
437 switch (lr->core.rotation & 3) {
438 case 1: u = 1.0f - sy; v = sx; break; // 90° CCW
439 case 2: u = 1.0f - sx; v = 1.0f - sy; break; // 180°
440 case 3: u = sy; v = 1.0f - sx; break; // 270° CCW
441 default: u = sx; v = sy; break; // 0°
442 }
443
444 float n = (id == RETRO_DEVICE_ID_POINTER_X ? u : v) * 2.0f - 1.0f;
445 return (int16_t)(SDL_clamp(n, -1.0f, 1.0f) * 0x7FFF);
446 }
447 case RETRO_DEVICE_ID_POINTER_PRESSED:
448 case RETRO_DEVICE_ID_POINTER_COUNT: {
449 Uint32 state = SDL_GetMouseState(NULL, NULL);
450 return (state & SDL_BUTTON_LMASK) ? 1 : 0;
451 }
452 case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
453 return inside ? 0 : 1;
454 }
455 return 0;
456 }
457
458 case RETRO_DEVICE_LIGHTGUN: {
459 if (id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X || id == RETRO_DEVICE_ID_LIGHTGUN_X) {
460 int w = 0, h = 0;
461 if (lr->window) SDL_GetWindowSize(lr->window, &w, &h);
462 if (w > 0) return (int16_t)((lr->core.inputMouseX / (float)w) * 32767.0f * 2.0f - 32767.0f);
463 }
464 if (id == RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y || id == RETRO_DEVICE_ID_LIGHTGUN_Y) {
465 int w = 0, h = 0;
466 if (lr->window) SDL_GetWindowSize(lr->window, &w, &h);
467 if (h > 0) return (int16_t)((lr->core.inputMouseY / (float)h) * 32767.0f * 2.0f - 32767.0f);
468 }
469 if (id == RETRO_DEVICE_ID_LIGHTGUN_TRIGGER) {
470 Uint32 state = SDL_GetMouseState(NULL, NULL);
471 return (state & SDL_BUTTON_LMASK) ? 1 : 0;
472 }
473 return 0;
474 }
475
476 default:
477 return 0;
478 }
479}
480
484void SDL_Libretro_HandleEvent(SDL_Libretro* lr, const SDL_Event* event) {
485 if (!lr || !event) return;
486
487 switch (event->type) {
488 case SDL_EVENT_GAMEPAD_ADDED: {
489 SDL_JoystickID jid = event->gdevice.which;
490 SDL_Gamepad* gp = SDL_OpenGamepad(jid);
491 if (gp) {
492 for (unsigned i = 0; i < 16; i++) {
493 if (!lr->gamepads[i]) {
494 lr->gamepads[i] = gp;
495 const char* name = SDL_GetGamepadName(gp);
496 if (i + 1 > lr->gamepadCount) lr->gamepadCount = i + 1;
497 SDL_Log("[SDL_Libretro] Gamepad added: #%u %s", i + 1, name ? name : "");
498 break;
499 }
500 }
501 }
502 break;
503 }
504 case SDL_EVENT_GAMEPAD_REMOVED: {
505 SDL_JoystickID jid = event->gdevice.which;
506 for (unsigned i = 0; i < 16; i++) {
507 if (lr->gamepads[i] && SDL_GetGamepadID(lr->gamepads[i]) == jid) {
508 const char* name = SDL_GetGamepadName(lr->gamepads[i]);
509 SDL_Log("[SDL_Libretro] Gamepad removed: #%u %s", i + 1, name ? name : "");
510 SDL_CloseGamepad(lr->gamepads[i]);
511 lr->gamepads[i] = NULL;
512 break;
513 }
514 }
515 break;
516 }
517 case SDL_EVENT_KEY_DOWN:
518 case SDL_EVENT_KEY_UP: {
519 if (lr->core.keyboard_event) {
520 bool down = (event->type == SDL_EVENT_KEY_DOWN);
521 unsigned retrokey = SDL_Libretro_ScancodeToRetroKey(event->key.scancode);
522 uint16_t modmask = SDL_Libretro_KeymodToRetroMod(event->key.mod);
523
524 // Determine the character if one is available for the key event.
525 uint32_t character = 0;
526 if (event->key.key > 0x20 && event->key.key < 0x110000 &&
527 !(event->key.key & SDLK_SCANCODE_MASK)) {
528 character = (uint32_t)event->key.key;
529 }
530
531 lr->core.keyboard_event(down, retrokey, character, modmask);
532 }
533 break;
534 }
535 default:
536 break;
537 }
538}
539
540bool SDL_Libretro_SetPortDevice(SDL_Libretro* lr, unsigned port, unsigned device) {
541 if (!lr || port >= 16) return false;
542 lr->core.portDeviceMap[port] = device;
543 if (lr->core.loaded) {
544 lr->core.symbols.retro_set_controller_port_device(port, device);
545 }
546 return true;
547}
548
559unsigned SDL_Libretro_GetPortDevice(const SDL_Libretro* lr, unsigned port) {
560 if (!lr || port >= 16) return RETRO_DEVICE_NONE;
561 return lr->core.portDeviceMap[port];
562}
563
564void SDL_Libretro_SetKeyboardMapping(SDL_Libretro* lr, int retroButton, SDL_Scancode scancode) {
565 if (!lr || retroButton < 0 || retroButton > RETRO_DEVICE_ID_JOYPAD_R3) return;
566 lr->keyboardPlayer1[retroButton] = scancode;
567}
568
569void SDL_Libretro_SetVirtualButton(SDL_Libretro* lr, unsigned port, int button, bool pressed) {
570 if (!lr || port >= 16 || button < 0 || button >= 16) return;
571 lr->core.virtualJoypadState[button] = pressed;
572}
573
574// Input Descriptors
575
583unsigned SDL_Libretro_GetInputDescriptorCount(const SDL_Libretro* lr) {
584 return (lr && lr->core.loaded) ? lr->core.inputDescriptorCount : 0;
585}
586
605bool SDL_Libretro_GetInputDescriptor(const SDL_Libretro* lr, unsigned index, unsigned* port, unsigned* device, unsigned* id, const char** description) {
606 if (!lr || !lr->core.loaded || index >= lr->core.inputDescriptorCount) return false;
607 const struct retro_input_descriptor* d = &lr->core.inputDescriptors[index];
608 if (port) *port = d->port;
609 if (device) *device = d->device;
610 if (id) *id = d->id;
611 if (description) *description = d->description;
612 return true;
613}
614
615// Sensors
616
617static SDL_Sensor* SDL_Libretro_OpenSensorByType(SDL_SensorType type) {
618 int count = 0;
619 SDL_SensorID* sensors = SDL_GetSensors(&count);
620 if (!sensors) return NULL;
621 SDL_Sensor* result = NULL;
622 for (int i = 0; i < count; i++) {
623 if (SDL_GetSensorTypeForID(sensors[i]) == type) {
624 result = SDL_OpenSensor(sensors[i]);
625 break;
626 }
627 }
628 SDL_free(sensors);
629 return result;
630}
631
632static bool SDL_Libretro_SetSensorState(unsigned port, enum retro_sensor_action action, unsigned rate) {
633 SDL_Libretro* lr = SDL_Libretro_active;
634 if (!lr || port >= SDL_LIBRETRO_SENSOR_PORTS) return false;
635 (void)rate;
636
637 switch (action) {
638 case RETRO_SENSOR_ACCELEROMETER_ENABLE:
639 if (!lr->core.sensorAccel[port]) {
640 lr->core.sensorAccel[port] = SDL_Libretro_OpenSensorByType(SDL_SENSOR_ACCEL);
641 }
642 return lr->core.sensorAccel[port] != NULL;
643 case RETRO_SENSOR_ACCELEROMETER_DISABLE:
644 if (lr->core.sensorAccel[port]) {
645 SDL_CloseSensor(lr->core.sensorAccel[port]);
646 lr->core.sensorAccel[port] = NULL;
647 }
648 SDL_memset(lr->core.sensorAccelData[port], 0, sizeof(lr->core.sensorAccelData[port]));
649 return true;
650 case RETRO_SENSOR_GYROSCOPE_ENABLE:
651 if (!lr->core.sensorGyro[port]) {
652 lr->core.sensorGyro[port] = SDL_Libretro_OpenSensorByType(SDL_SENSOR_GYRO);
653 }
654 return lr->core.sensorGyro[port] != NULL;
655 case RETRO_SENSOR_GYROSCOPE_DISABLE:
656 if (lr->core.sensorGyro[port]) {
657 SDL_CloseSensor(lr->core.sensorGyro[port]);
658 lr->core.sensorGyro[port] = NULL;
659 }
660 SDL_memset(lr->core.sensorGyroData[port], 0, sizeof(lr->core.sensorGyroData[port]));
661 return true;
662 // SDL3 does not currently support luminance sensors.
663 // case RETRO_SENSOR_ILLUMINANCE_ENABLE:
664 // case RETRO_SENSOR_ILLUMINANCE_DISABLE:
665 default:
666 return false;
667 }
668}
669
670static float SDL_Libretro_GetSensorInput(unsigned port, unsigned id) {
671 SDL_Libretro* lr = SDL_Libretro_active;
672 if (!lr || port >= SDL_LIBRETRO_SENSOR_PORTS) return 0.0f;
673
674 // Accelerometer
675 if (id >= RETRO_SENSOR_ACCELEROMETER_X && id <= RETRO_SENSOR_ACCELEROMETER_Z && lr->core.sensorAccel[port]) {
676 float data[3];
677 if (SDL_GetSensorData(lr->core.sensorAccel[port], data, 3)) {
678 lr->core.sensorAccelData[port][0] = data[0] / SDL_STANDARD_GRAVITY;
679 lr->core.sensorAccelData[port][1] = data[1] / SDL_STANDARD_GRAVITY;
680 lr->core.sensorAccelData[port][2] = data[2] / SDL_STANDARD_GRAVITY;
681 }
682 return lr->core.sensorAccelData[port][id - RETRO_SENSOR_ACCELEROMETER_X];
683 }
684
685 // Gyroscope
686 if (id >= RETRO_SENSOR_GYROSCOPE_X && id <= RETRO_SENSOR_GYROSCOPE_Z && lr->core.sensorGyro[port]) {
687 float data[3];
688 if (SDL_GetSensorData(lr->core.sensorGyro[port], data, 3)) {
689 lr->core.sensorGyroData[port][0] = data[0];
690 lr->core.sensorGyroData[port][1] = data[1];
691 lr->core.sensorGyroData[port][2] = data[2];
692 }
693 return lr->core.sensorGyroData[port][id - RETRO_SENSOR_GYROSCOPE_X];
694 }
695
696 // Luminance: SDL3 does not currently support luminance sensors.
697
698 return 0.0f;
699}
700
701static void SDL_Libretro_CloseSensors(SDL_Libretro* lr) {
702 if (!lr) return;
703
704 for (unsigned i = 0; i < SDL_LIBRETRO_SENSOR_PORTS; i++) {
705 // Accelerometer
706 if (lr->core.sensorAccel[i]) {
707 SDL_CloseSensor(lr->core.sensorAccel[i]);
708 lr->core.sensorAccel[i] = NULL;
709 }
710 lr->core.sensorAccelData[i][0] = 0.0f;
711 lr->core.sensorAccelData[i][1] = 0.0f;
712 lr->core.sensorAccelData[i][2] = 0.0f;
713
714 // Gyroscope
715 if (lr->core.sensorGyro[i]) {
716 SDL_CloseSensor(lr->core.sensorGyro[i]);
717 lr->core.sensorGyro[i] = NULL;
718 }
719 lr->core.sensorGyroData[i][0] = 0.0f;
720 lr->core.sensorGyroData[i][1] = 0.0f;
721 lr->core.sensorGyroData[i][2] = 0.0f;
722 }
723}
724
725#endif /* SDL_LIBRETRO_INPUT_IMPL_ONCE */
unsigned SDL_Libretro_GetInputDescriptorCount(const SDL_Libretro *lr)
Retrieve the number of input descriptors provided by a Libretro core.
unsigned SDL_Libretro_GetPortDevice(const SDL_Libretro *lr, unsigned port)
Get the device type assigned to a controller port.
bool SDL_Libretro_GetInputDescriptor(const SDL_Libretro *lr, unsigned index, unsigned *port, unsigned *device, unsigned *id, const char **description)
Retrieve an input descriptor by index.
void SDL_Libretro_HandleEvent(SDL_Libretro *lr, const SDL_Event *event)
Call this function within the SDL_PollEvent() loop to ensure libretro knows of important events.