SDL_Libretro 0.1.0
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
14static const SDL_GamepadButton SDL_Libretro_DefaultGamepadButtons[SDL_LIBRETRO_MAX_JOYPAD_BUTTONS] = {
15 SDL_GAMEPAD_BUTTON_SOUTH, // B
16 SDL_GAMEPAD_BUTTON_WEST, // Y
17 SDL_GAMEPAD_BUTTON_BACK, // Select
18 SDL_GAMEPAD_BUTTON_START, // Start
19 SDL_GAMEPAD_BUTTON_DPAD_UP, // Up
20 SDL_GAMEPAD_BUTTON_DPAD_DOWN, // Down
21 SDL_GAMEPAD_BUTTON_DPAD_LEFT, // Left
22 SDL_GAMEPAD_BUTTON_DPAD_RIGHT, // Right
23 SDL_GAMEPAD_BUTTON_EAST, // A
24 SDL_GAMEPAD_BUTTON_NORTH, // X
25 SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, // L
26 SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, // R
27 SDL_GAMEPAD_BUTTON_LEFT_PADDLE1, // L2, normally driven by the trigger axis
28 SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1, // R2, normally driven by the trigger axis
29 SDL_GAMEPAD_BUTTON_LEFT_STICK, // L3
30 SDL_GAMEPAD_BUTTON_RIGHT_STICK, // R3
31};
32
40static SDL_GamepadButton SDL_Libretro_RetroJoypadToGamepadButton(const SDL_Libretro* lr, unsigned port, unsigned button) {
41 return port < SDL_LIBRETRO_MAX_USERS && button < SDL_LIBRETRO_MAX_JOYPAD_BUTTONS
42 ? lr->gamepadButtons[port][button]
43 : SDL_GAMEPAD_BUTTON_INVALID;
44}
45
53static bool SDL_Libretro_AnalogToDigitalPressed(const SDL_Libretro* lr, unsigned port, unsigned id) {
54 SDL_LibretroAnalogToDigital stick = lr->analogToDigital[port];
56 return false;
57 }
58 bool right = stick == SDL_LIBRETRO_ANALOG_TO_DIGITAL_RIGHT;
59 SDL_GamepadAxis axisX = right ? SDL_GAMEPAD_AXIS_RIGHTX : SDL_GAMEPAD_AXIS_LEFTX;
60 SDL_GamepadAxis axisY = right ? SDL_GAMEPAD_AXIS_RIGHTY : SDL_GAMEPAD_AXIS_LEFTY;
61 switch (id) {
62 case RETRO_DEVICE_ID_JOYPAD_UP: return SDL_GetGamepadAxis(lr->gamepads[port], axisY) < -8192;
63 case RETRO_DEVICE_ID_JOYPAD_DOWN: return SDL_GetGamepadAxis(lr->gamepads[port], axisY) > 8192;
64 case RETRO_DEVICE_ID_JOYPAD_LEFT: return SDL_GetGamepadAxis(lr->gamepads[port], axisX) < -8192;
65 case RETRO_DEVICE_ID_JOYPAD_RIGHT: return SDL_GetGamepadAxis(lr->gamepads[port], axisX) > 8192;
66 default: return false;
67 }
68}
69
78static SDL_Scancode SDL_Libretro_RetroKeyToScancode(unsigned key) {
79 switch (key) {
80 case RETROK_BACKSPACE: return SDL_SCANCODE_BACKSPACE;
81 case RETROK_TAB: return SDL_SCANCODE_TAB;
82 case RETROK_RETURN: return SDL_SCANCODE_RETURN;
83 case RETROK_ESCAPE: return SDL_SCANCODE_ESCAPE;
84 case RETROK_SPACE: return SDL_SCANCODE_SPACE;
85 case RETROK_EXCLAIM: return SDL_SCANCODE_1;
86 case RETROK_QUOTEDBL: return SDL_SCANCODE_APOSTROPHE;
87 case RETROK_HASH: return SDL_SCANCODE_3;
88 case RETROK_DOLLAR: return SDL_SCANCODE_4;
89 case RETROK_AMPERSAND: return SDL_SCANCODE_7;
90 case RETROK_QUOTE: return SDL_SCANCODE_APOSTROPHE;
91 case RETROK_LEFTPAREN: return SDL_SCANCODE_9;
92 case RETROK_RIGHTPAREN: return SDL_SCANCODE_0;
93 case RETROK_ASTERISK: return SDL_SCANCODE_8;
94 case RETROK_PLUS: return SDL_SCANCODE_EQUALS;
95 case RETROK_COMMA: return SDL_SCANCODE_COMMA;
96 case RETROK_MINUS: return SDL_SCANCODE_MINUS;
97 case RETROK_PERIOD: return SDL_SCANCODE_PERIOD;
98 case RETROK_SLASH: return SDL_SCANCODE_SLASH;
99 case RETROK_0: return SDL_SCANCODE_0;
100 case RETROK_1: return SDL_SCANCODE_1;
101 case RETROK_2: return SDL_SCANCODE_2;
102 case RETROK_3: return SDL_SCANCODE_3;
103 case RETROK_4: return SDL_SCANCODE_4;
104 case RETROK_5: return SDL_SCANCODE_5;
105 case RETROK_6: return SDL_SCANCODE_6;
106 case RETROK_7: return SDL_SCANCODE_7;
107 case RETROK_8: return SDL_SCANCODE_8;
108 case RETROK_9: return SDL_SCANCODE_9;
109 case RETROK_SEMICOLON: return SDL_SCANCODE_SEMICOLON;
110 case RETROK_EQUALS: return SDL_SCANCODE_EQUALS;
111 case RETROK_LEFTBRACKET: return SDL_SCANCODE_LEFTBRACKET;
112 case RETROK_BACKSLASH: return SDL_SCANCODE_BACKSLASH;
113 case RETROK_RIGHTBRACKET: return SDL_SCANCODE_RIGHTBRACKET;
114 case RETROK_BACKQUOTE: return SDL_SCANCODE_GRAVE;
115 case RETROK_a: return SDL_SCANCODE_A;
116 case RETROK_b: return SDL_SCANCODE_B;
117 case RETROK_c: return SDL_SCANCODE_C;
118 case RETROK_d: return SDL_SCANCODE_D;
119 case RETROK_e: return SDL_SCANCODE_E;
120 case RETROK_f: return SDL_SCANCODE_F;
121 case RETROK_g: return SDL_SCANCODE_G;
122 case RETROK_h: return SDL_SCANCODE_H;
123 case RETROK_i: return SDL_SCANCODE_I;
124 case RETROK_j: return SDL_SCANCODE_J;
125 case RETROK_k: return SDL_SCANCODE_K;
126 case RETROK_l: return SDL_SCANCODE_L;
127 case RETROK_m: return SDL_SCANCODE_M;
128 case RETROK_n: return SDL_SCANCODE_N;
129 case RETROK_o: return SDL_SCANCODE_O;
130 case RETROK_p: return SDL_SCANCODE_P;
131 case RETROK_q: return SDL_SCANCODE_Q;
132 case RETROK_r: return SDL_SCANCODE_R;
133 case RETROK_s: return SDL_SCANCODE_S;
134 case RETROK_t: return SDL_SCANCODE_T;
135 case RETROK_u: return SDL_SCANCODE_U;
136 case RETROK_v: return SDL_SCANCODE_V;
137 case RETROK_w: return SDL_SCANCODE_W;
138 case RETROK_x: return SDL_SCANCODE_X;
139 case RETROK_y: return SDL_SCANCODE_Y;
140 case RETROK_z: return SDL_SCANCODE_Z;
141 case RETROK_DELETE: return SDL_SCANCODE_DELETE;
142 case RETROK_KP0: return SDL_SCANCODE_KP_0;
143 case RETROK_KP1: return SDL_SCANCODE_KP_1;
144 case RETROK_KP2: return SDL_SCANCODE_KP_2;
145 case RETROK_KP3: return SDL_SCANCODE_KP_3;
146 case RETROK_KP4: return SDL_SCANCODE_KP_4;
147 case RETROK_KP5: return SDL_SCANCODE_KP_5;
148 case RETROK_KP6: return SDL_SCANCODE_KP_6;
149 case RETROK_KP7: return SDL_SCANCODE_KP_7;
150 case RETROK_KP8: return SDL_SCANCODE_KP_8;
151 case RETROK_KP9: return SDL_SCANCODE_KP_9;
152 case RETROK_KP_PERIOD: return SDL_SCANCODE_KP_PERIOD;
153 case RETROK_KP_DIVIDE: return SDL_SCANCODE_KP_DIVIDE;
154 case RETROK_KP_MULTIPLY: return SDL_SCANCODE_KP_MULTIPLY;
155 case RETROK_KP_MINUS: return SDL_SCANCODE_KP_MINUS;
156 case RETROK_KP_PLUS: return SDL_SCANCODE_KP_PLUS;
157 case RETROK_KP_ENTER: return SDL_SCANCODE_KP_ENTER;
158 case RETROK_KP_EQUALS: return SDL_SCANCODE_KP_EQUALS;
159 case RETROK_UP: return SDL_SCANCODE_UP;
160 case RETROK_DOWN: return SDL_SCANCODE_DOWN;
161 case RETROK_RIGHT: return SDL_SCANCODE_RIGHT;
162 case RETROK_LEFT: return SDL_SCANCODE_LEFT;
163 case RETROK_INSERT: return SDL_SCANCODE_INSERT;
164 case RETROK_HOME: return SDL_SCANCODE_HOME;
165 case RETROK_END: return SDL_SCANCODE_END;
166 case RETROK_PAGEUP: return SDL_SCANCODE_PAGEUP;
167 case RETROK_PAGEDOWN: return SDL_SCANCODE_PAGEDOWN;
168 case RETROK_F1: return SDL_SCANCODE_F1;
169 case RETROK_F2: return SDL_SCANCODE_F2;
170 case RETROK_F3: return SDL_SCANCODE_F3;
171 case RETROK_F4: return SDL_SCANCODE_F4;
172 case RETROK_F5: return SDL_SCANCODE_F5;
173 case RETROK_F6: return SDL_SCANCODE_F6;
174 case RETROK_F7: return SDL_SCANCODE_F7;
175 case RETROK_F8: return SDL_SCANCODE_F8;
176 case RETROK_F9: return SDL_SCANCODE_F9;
177 case RETROK_F10: return SDL_SCANCODE_F10;
178 case RETROK_F11: return SDL_SCANCODE_F11;
179 case RETROK_F12: return SDL_SCANCODE_F12;
180 case RETROK_F13: return SDL_SCANCODE_F13;
181 case RETROK_F14: return SDL_SCANCODE_F14;
182 case RETROK_F15: return SDL_SCANCODE_F15;
183 case RETROK_NUMLOCK: return SDL_SCANCODE_NUMLOCKCLEAR;
184 case RETROK_CAPSLOCK: return SDL_SCANCODE_CAPSLOCK;
185 case RETROK_SCROLLOCK: return SDL_SCANCODE_SCROLLLOCK;
186 case RETROK_RSHIFT: return SDL_SCANCODE_RSHIFT;
187 case RETROK_LSHIFT: return SDL_SCANCODE_LSHIFT;
188 case RETROK_RCTRL: return SDL_SCANCODE_RCTRL;
189 case RETROK_LCTRL: return SDL_SCANCODE_LCTRL;
190 case RETROK_RALT: return SDL_SCANCODE_RALT;
191 case RETROK_LALT: return SDL_SCANCODE_LALT;
192 case RETROK_RMETA: return SDL_SCANCODE_RGUI;
193 case RETROK_LMETA: return SDL_SCANCODE_LGUI;
194 case RETROK_RSUPER: return SDL_SCANCODE_RGUI;
195 case RETROK_LSUPER: return SDL_SCANCODE_LGUI;
196 case RETROK_MENU: return SDL_SCANCODE_MENU;
197 default: return SDL_SCANCODE_UNKNOWN;
198 }
199}
200
209static unsigned SDL_Libretro_ScancodeToRetroKey(SDL_Scancode scancode) {
210 switch (scancode) {
211 case SDL_SCANCODE_BACKSPACE: return RETROK_BACKSPACE;
212 case SDL_SCANCODE_TAB: return RETROK_TAB;
213 case SDL_SCANCODE_RETURN: return RETROK_RETURN;
214 case SDL_SCANCODE_ESCAPE: return RETROK_ESCAPE;
215 case SDL_SCANCODE_SPACE: return RETROK_SPACE;
216 case SDL_SCANCODE_APOSTROPHE: return RETROK_QUOTE;
217 case SDL_SCANCODE_COMMA: return RETROK_COMMA;
218 case SDL_SCANCODE_MINUS: return RETROK_MINUS;
219 case SDL_SCANCODE_PERIOD: return RETROK_PERIOD;
220 case SDL_SCANCODE_SLASH: return RETROK_SLASH;
221 case SDL_SCANCODE_0: return RETROK_0;
222 case SDL_SCANCODE_1: return RETROK_1;
223 case SDL_SCANCODE_2: return RETROK_2;
224 case SDL_SCANCODE_3: return RETROK_3;
225 case SDL_SCANCODE_4: return RETROK_4;
226 case SDL_SCANCODE_5: return RETROK_5;
227 case SDL_SCANCODE_6: return RETROK_6;
228 case SDL_SCANCODE_7: return RETROK_7;
229 case SDL_SCANCODE_8: return RETROK_8;
230 case SDL_SCANCODE_9: return RETROK_9;
231 case SDL_SCANCODE_SEMICOLON: return RETROK_SEMICOLON;
232 case SDL_SCANCODE_EQUALS: return RETROK_EQUALS;
233 case SDL_SCANCODE_LEFTBRACKET: return RETROK_LEFTBRACKET;
234 case SDL_SCANCODE_BACKSLASH: return RETROK_BACKSLASH;
235 case SDL_SCANCODE_RIGHTBRACKET: return RETROK_RIGHTBRACKET;
236 case SDL_SCANCODE_GRAVE: return RETROK_BACKQUOTE;
237 case SDL_SCANCODE_A: return RETROK_a;
238 case SDL_SCANCODE_B: return RETROK_b;
239 case SDL_SCANCODE_C: return RETROK_c;
240 case SDL_SCANCODE_D: return RETROK_d;
241 case SDL_SCANCODE_E: return RETROK_e;
242 case SDL_SCANCODE_F: return RETROK_f;
243 case SDL_SCANCODE_G: return RETROK_g;
244 case SDL_SCANCODE_H: return RETROK_h;
245 case SDL_SCANCODE_I: return RETROK_i;
246 case SDL_SCANCODE_J: return RETROK_j;
247 case SDL_SCANCODE_K: return RETROK_k;
248 case SDL_SCANCODE_L: return RETROK_l;
249 case SDL_SCANCODE_M: return RETROK_m;
250 case SDL_SCANCODE_N: return RETROK_n;
251 case SDL_SCANCODE_O: return RETROK_o;
252 case SDL_SCANCODE_P: return RETROK_p;
253 case SDL_SCANCODE_Q: return RETROK_q;
254 case SDL_SCANCODE_R: return RETROK_r;
255 case SDL_SCANCODE_S: return RETROK_s;
256 case SDL_SCANCODE_T: return RETROK_t;
257 case SDL_SCANCODE_U: return RETROK_u;
258 case SDL_SCANCODE_V: return RETROK_v;
259 case SDL_SCANCODE_W: return RETROK_w;
260 case SDL_SCANCODE_X: return RETROK_x;
261 case SDL_SCANCODE_Y: return RETROK_y;
262 case SDL_SCANCODE_Z: return RETROK_z;
263 case SDL_SCANCODE_DELETE: return RETROK_DELETE;
264 case SDL_SCANCODE_KP_0: return RETROK_KP0;
265 case SDL_SCANCODE_KP_1: return RETROK_KP1;
266 case SDL_SCANCODE_KP_2: return RETROK_KP2;
267 case SDL_SCANCODE_KP_3: return RETROK_KP3;
268 case SDL_SCANCODE_KP_4: return RETROK_KP4;
269 case SDL_SCANCODE_KP_5: return RETROK_KP5;
270 case SDL_SCANCODE_KP_6: return RETROK_KP6;
271 case SDL_SCANCODE_KP_7: return RETROK_KP7;
272 case SDL_SCANCODE_KP_8: return RETROK_KP8;
273 case SDL_SCANCODE_KP_9: return RETROK_KP9;
274 case SDL_SCANCODE_KP_PERIOD: return RETROK_KP_PERIOD;
275 case SDL_SCANCODE_KP_DIVIDE: return RETROK_KP_DIVIDE;
276 case SDL_SCANCODE_KP_MULTIPLY: return RETROK_KP_MULTIPLY;
277 case SDL_SCANCODE_KP_MINUS: return RETROK_KP_MINUS;
278 case SDL_SCANCODE_KP_PLUS: return RETROK_KP_PLUS;
279 case SDL_SCANCODE_KP_ENTER: return RETROK_KP_ENTER;
280 case SDL_SCANCODE_KP_EQUALS: return RETROK_KP_EQUALS;
281 case SDL_SCANCODE_UP: return RETROK_UP;
282 case SDL_SCANCODE_DOWN: return RETROK_DOWN;
283 case SDL_SCANCODE_RIGHT: return RETROK_RIGHT;
284 case SDL_SCANCODE_LEFT: return RETROK_LEFT;
285 case SDL_SCANCODE_INSERT: return RETROK_INSERT;
286 case SDL_SCANCODE_HOME: return RETROK_HOME;
287 case SDL_SCANCODE_END: return RETROK_END;
288 case SDL_SCANCODE_PAGEUP: return RETROK_PAGEUP;
289 case SDL_SCANCODE_PAGEDOWN: return RETROK_PAGEDOWN;
290 case SDL_SCANCODE_F1: return RETROK_F1;
291 case SDL_SCANCODE_F2: return RETROK_F2;
292 case SDL_SCANCODE_F3: return RETROK_F3;
293 case SDL_SCANCODE_F4: return RETROK_F4;
294 case SDL_SCANCODE_F5: return RETROK_F5;
295 case SDL_SCANCODE_F6: return RETROK_F6;
296 case SDL_SCANCODE_F7: return RETROK_F7;
297 case SDL_SCANCODE_F8: return RETROK_F8;
298 case SDL_SCANCODE_F9: return RETROK_F9;
299 case SDL_SCANCODE_F10: return RETROK_F10;
300 case SDL_SCANCODE_F11: return RETROK_F11;
301 case SDL_SCANCODE_F12: return RETROK_F12;
302 case SDL_SCANCODE_F13: return RETROK_F13;
303 case SDL_SCANCODE_F14: return RETROK_F14;
304 case SDL_SCANCODE_F15: return RETROK_F15;
305 case SDL_SCANCODE_NUMLOCKCLEAR: return RETROK_NUMLOCK;
306 case SDL_SCANCODE_CAPSLOCK: return RETROK_CAPSLOCK;
307 case SDL_SCANCODE_SCROLLLOCK: return RETROK_SCROLLOCK;
308 case SDL_SCANCODE_RSHIFT: return RETROK_RSHIFT;
309 case SDL_SCANCODE_LSHIFT: return RETROK_LSHIFT;
310 case SDL_SCANCODE_RCTRL: return RETROK_RCTRL;
311 case SDL_SCANCODE_LCTRL: return RETROK_LCTRL;
312 case SDL_SCANCODE_RALT: return RETROK_RALT;
313 case SDL_SCANCODE_LALT: return RETROK_LALT;
314 case SDL_SCANCODE_RGUI: return RETROK_RMETA;
315 case SDL_SCANCODE_LGUI: return RETROK_LMETA;
316 case SDL_SCANCODE_MENU: return RETROK_MENU;
317 default: return RETROK_UNKNOWN;
318 }
319}
320
325static uint16_t SDL_Libretro_KeymodToRetroMod(SDL_Keymod mod) {
326 uint16_t retromod = RETROKMOD_NONE;
327 if (mod & SDL_KMOD_SHIFT) retromod |= RETROKMOD_SHIFT;
328 if (mod & SDL_KMOD_CTRL) retromod |= RETROKMOD_CTRL;
329 if (mod & SDL_KMOD_ALT) retromod |= RETROKMOD_ALT;
330 if (mod & SDL_KMOD_GUI) retromod |= RETROKMOD_META;
331 if (mod & SDL_KMOD_NUM) retromod |= RETROKMOD_NUMLOCK;
332 if (mod & SDL_KMOD_CAPS) retromod |= RETROKMOD_CAPSLOCK;
333 if (mod & SDL_KMOD_SCROLL) retromod |= RETROKMOD_SCROLLOCK;
334 return retromod;
335}
336
337static void SDL_Libretro_InputPoll(void) {
338 SDL_Libretro* lr = SDL_Libretro_active;
339 if (!lr) return;
340
341 // Mouse Position
342 SDL_GetMouseState(&lr->core.inputMouseX, &lr->core.inputMouseY);
343
344 // Relative Mouse Motion Accumulator; whole pixels are reported this frame
345 // and the sub-pixel remainder carries over so slow motion isn't lost.
346 lr->core.inputMotionX = SDL_truncf(lr->core.inputMotionAccumX);
347 lr->core.inputMotionY = SDL_truncf(lr->core.inputMotionAccumY);
348 lr->core.inputMotionAccumX -= lr->core.inputMotionX;
349 lr->core.inputMotionAccumY -= lr->core.inputMotionY;
350
351 // Mouse Wheel Accumulator
352 lr->core.inputWheelX = lr->core.inputWheelAccumX;
353 lr->core.inputWheelY = lr->core.inputWheelAccumY;
354 lr->core.inputWheelAccumX = 0.0f;
355 lr->core.inputWheelAccumY = 0.0f;
356}
357
358static bool SDL_Libretro_IsKeyDown(SDL_Scancode scancode) {
359 // A custom binding can be any number, so stay within the state array.
360 int numKeys = 0;
361 const bool* state = SDL_GetKeyboardState(&numKeys);
362 return scancode > SDL_SCANCODE_UNKNOWN && (int)scancode < numKeys && state[scancode];
363}
364
376static bool SDL_Libretro_PointerCoords(const SDL_Libretro* lr, int16_t* outX, int16_t* outY) {
377 float mx = lr->core.inputMouseX;
378 float my = lr->core.inputMouseY;
379
380 // The mouse is in window coordinates, while renderDstRect is in render
381 // coordinates (HiDPI pixel density, logical presentation).
382 if (lr->renderer) {
383 SDL_RenderCoordinatesFromWindow(lr->renderer, mx, my, &mx, &my);
384 }
385
386 SDL_FRect r = lr->core.renderDstRect;
387 bool inside = r.w > 0.0f && r.h > 0.0f &&
388 mx >= r.x && mx < r.x + r.w &&
389 my >= r.y && my < r.y + r.h;
390
391 if (r.w <= 0.0f || r.h <= 0.0f) {
392 if (outX) *outX = 0;
393 if (outY) *outY = 0;
394 return false;
395 }
396
397 // Position within the on-screen image from 0 to 1.
398 float sx = (mx - r.x) / r.w;
399 float sy = (my - r.y) / r.h;
400
401 // Fix the coordinates based on the current rotation.
402 float u, v;
403 switch (lr->core.rotation & 3) {
404 case 1:
405 u = 1.0f - sy;
406 v = sx;
407 break; // 90' CCW
408 case 2:
409 u = 1.0f - sx;
410 v = 1.0f - sy;
411 break; // 180'
412 case 3:
413 u = sy;
414 v = 1.0f - sx;
415 break; // 270' CCW
416 default:
417 u = sx;
418 v = sy;
419 break; // 0'
420 }
421
422 if (outX) *outX = (int16_t)(SDL_clamp(u * 2.0f - 1.0f, -1.0f, 1.0f) * 0x7FFF);
423 if (outY) *outY = (int16_t)(SDL_clamp(v * 2.0f - 1.0f, -1.0f, 1.0f) * 0x7FFF);
424 return inside;
425}
426
427static int16_t SDL_Libretro_InputState(unsigned port, unsigned device, unsigned index, unsigned id) {
428 SDL_Libretro* lr = SDL_Libretro_active;
429 if (!lr) return 0;
430
431 // During a backward step's throwaway re-run, report neutral input so the
432 // discarded frame doesn't react to currently-held buttons.
433 if (lr->rewindActive) return 0;
434
435 unsigned baseDevice = device & RETRO_DEVICE_MASK;
436
437 switch (baseDevice) {
438 case RETRO_DEVICE_JOYPAD: {
439 if (id == RETRO_DEVICE_ID_JOYPAD_MASK) {
440 int16_t mask = 0;
441 for (unsigned b = 0; b <= RETRO_DEVICE_ID_JOYPAD_R3; b++) {
442 if (SDL_Libretro_InputState(port, device, index, b)) {
443 mask |= (1 << b);
444 }
445 }
446 return mask;
447 }
448
449 // Virtual joypad (per-port)
450 if (port < SDL_LIBRETRO_MAX_USERS && id < SDL_LIBRETRO_MAX_JOYPAD_BUTTONS &&
451 lr->core.virtualJoypadState[port][id]) {
452 return 1;
453 }
454
455 // Keyboard mapping (port 0)
456 if (port == 0 && id <= RETRO_DEVICE_ID_JOYPAD_R3) {
457 if (SDL_Libretro_IsKeyDown(lr->keyboardPlayer1[id])) {
458 return 1;
459 }
460 }
461
462 // Gamepad
463 if (port < SDL_LIBRETRO_MAX_USERS && lr->gamepads[port]) {
464 // L2/R2 are axes first; a bound button can also drive them.
465 if (id == RETRO_DEVICE_ID_JOYPAD_L2 &&
466 SDL_GetGamepadAxis(lr->gamepads[port], SDL_GAMEPAD_AXIS_LEFT_TRIGGER) > 8192) {
467 return 1;
468 }
469 if (id == RETRO_DEVICE_ID_JOYPAD_R2 &&
470 SDL_GetGamepadAxis(lr->gamepads[port], SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) > 8192) {
471 return 1;
472 }
473
474 // The port's analog-to-digital stick can also push the D-Pad.
475 if (SDL_Libretro_AnalogToDigitalPressed(lr, port, id)) {
476 return 1;
477 }
478
479 SDL_GamepadButton btn = SDL_Libretro_RetroJoypadToGamepadButton(lr, port, id);
480 if (btn != SDL_GAMEPAD_BUTTON_INVALID) {
481 return SDL_GetGamepadButton(lr->gamepads[port], btn) ? 1 : 0;
482 }
483 }
484 return 0;
485 }
486
487 case RETRO_DEVICE_ANALOG: {
488 if (port < SDL_LIBRETRO_MAX_USERS && lr->gamepads[port]) {
489 SDL_Gamepad* gp = lr->gamepads[port];
490 if (index == RETRO_DEVICE_INDEX_ANALOG_LEFT ||
491 index == RETRO_DEVICE_INDEX_ANALOG_RIGHT) {
492 SDL_GamepadAxis axis = SDL_GAMEPAD_AXIS_INVALID;
493 if (index == RETRO_DEVICE_INDEX_ANALOG_LEFT) {
494 axis = (id == RETRO_DEVICE_ID_ANALOG_X) ? SDL_GAMEPAD_AXIS_LEFTX : SDL_GAMEPAD_AXIS_LEFTY;
495 }
496 else {
497 axis = (id == RETRO_DEVICE_ID_ANALOG_X) ? SDL_GAMEPAD_AXIS_RIGHTX : SDL_GAMEPAD_AXIS_RIGHTY;
498 }
499 return SDL_GetGamepadAxis(gp, axis);
500 }
501 if (index == RETRO_DEVICE_INDEX_ANALOG_BUTTON) {
502 // Per-button analog pressure. L2/R2 map to the trigger axes
503 // (0..0x7FFF); every other button is digital (0 or 0x7FFF).
504 if (id == RETRO_DEVICE_ID_JOYPAD_L2) {
505 return SDL_GetGamepadAxis(gp, SDL_GAMEPAD_AXIS_LEFT_TRIGGER);
506 }
507 if (id == RETRO_DEVICE_ID_JOYPAD_R2) {
508 return SDL_GetGamepadAxis(gp, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER);
509 }
510 SDL_GamepadButton btn = SDL_Libretro_RetroJoypadToGamepadButton(lr, port, id);
511 if (btn != SDL_GAMEPAD_BUTTON_INVALID && SDL_GetGamepadButton(gp, btn)) {
512 return 0x7FFF;
513 }
514 }
515 }
516 return 0;
517 }
518
519 case RETRO_DEVICE_KEYBOARD: {
520 SDL_Scancode sc = SDL_Libretro_RetroKeyToScancode(id);
521 return SDL_Libretro_IsKeyDown(sc) ? 1 : 0;
522 }
523
524 case RETRO_DEVICE_MOUSE: {
525 if (port != lr->mousePort) return 0;
526 switch (id) {
527 case RETRO_DEVICE_ID_MOUSE_X:
528 return (int16_t)SDL_clamp(lr->core.inputMotionX, -32768.0f, 32767.0f);
529 case RETRO_DEVICE_ID_MOUSE_Y:
530 return (int16_t)SDL_clamp(lr->core.inputMotionY, -32768.0f, 32767.0f);
531 case RETRO_DEVICE_ID_MOUSE_LEFT: {
532 Uint32 state = SDL_GetMouseState(NULL, NULL);
533 return (state & SDL_BUTTON_LMASK) ? 1 : 0;
534 }
535 case RETRO_DEVICE_ID_MOUSE_RIGHT: {
536 Uint32 state = SDL_GetMouseState(NULL, NULL);
537 return (state & SDL_BUTTON_RMASK) ? 1 : 0;
538 }
539 case RETRO_DEVICE_ID_MOUSE_MIDDLE: {
540 Uint32 state = SDL_GetMouseState(NULL, NULL);
541 return (state & SDL_BUTTON_MMASK) ? 1 : 0;
542 }
543 case RETRO_DEVICE_ID_MOUSE_BUTTON_4: {
544 Uint32 state = SDL_GetMouseState(NULL, NULL);
545 return (state & SDL_BUTTON_X1MASK) ? 1 : 0;
546 }
547 case RETRO_DEVICE_ID_MOUSE_BUTTON_5: {
548 Uint32 state = SDL_GetMouseState(NULL, NULL);
549 return (state & SDL_BUTTON_X2MASK) ? 1 : 0;
550 }
551 case RETRO_DEVICE_ID_MOUSE_WHEELUP:
552 return lr->core.inputWheelY > 0.0f ? 1 : 0;
553 case RETRO_DEVICE_ID_MOUSE_WHEELDOWN:
554 return lr->core.inputWheelY < 0.0f ? 1 : 0;
555 case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP:
556 return lr->core.inputWheelX > 0.0f ? 1 : 0;
557 case RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN:
558 return lr->core.inputWheelX < 0.0f ? 1 : 0;
559 }
560 return 0;
561 }
562
563 case RETRO_DEVICE_POINTER: {
564 if (port != lr->mousePort || index > 0) return 0;
565
566 switch (id) {
567 case RETRO_DEVICE_ID_POINTER_X: {
568 int16_t x = 0;
569 SDL_Libretro_PointerCoords(lr, &x, NULL);
570 return x;
571 }
572 case RETRO_DEVICE_ID_POINTER_Y: {
573 int16_t y = 0;
574 SDL_Libretro_PointerCoords(lr, NULL, &y);
575 return y;
576 }
577 case RETRO_DEVICE_ID_POINTER_PRESSED:
578 case RETRO_DEVICE_ID_POINTER_COUNT: {
579 Uint32 state = SDL_GetMouseState(NULL, NULL);
580 return (state & SDL_BUTTON_LMASK) ? 1 : 0;
581 }
582 case RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN:
583 return SDL_Libretro_PointerCoords(lr, NULL, NULL) ? 0 : 1;
584 }
585 return 0;
586 }
587
588 case RETRO_DEVICE_LIGHTGUN: {
589 if (port != lr->mousePort) return 0;
590 switch (id) {
591 // Aim maps through the render rect + rotation, like the pointer.
592 case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X:
593 case RETRO_DEVICE_ID_LIGHTGUN_X: {
594 int16_t x = 0;
595 SDL_Libretro_PointerCoords(lr, &x, NULL);
596 return x;
597 }
598 case RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y:
599 case RETRO_DEVICE_ID_LIGHTGUN_Y: {
600 int16_t y = 0;
601 SDL_Libretro_PointerCoords(lr, NULL, &y);
602 return y;
603 }
604 case RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN:
605 return SDL_Libretro_PointerCoords(lr, NULL, NULL) ? 0 : 1;
606
607 // Buttons: mouse buttons drive trigger/reload/aux; keyboard maps
608 // start/select and the d-pad.
609 case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER: {
610 Uint32 state = SDL_GetMouseState(NULL, NULL);
611 return (state & SDL_BUTTON_LMASK) ? 1 : 0;
612 }
613 case RETRO_DEVICE_ID_LIGHTGUN_RELOAD: {
614 // Reload is conventionally an offscreen trigger pull, and is
615 // also exposed on the right mouse button.
616 Uint32 state = SDL_GetMouseState(NULL, NULL);
617 if (state & SDL_BUTTON_RMASK) return 1;
618 if ((state & SDL_BUTTON_LMASK) && !SDL_Libretro_PointerCoords(lr, NULL, NULL)) return 1;
619 return 0;
620 }
621 case RETRO_DEVICE_ID_LIGHTGUN_AUX_A: {
622 Uint32 state = SDL_GetMouseState(NULL, NULL);
623 return (state & SDL_BUTTON_MMASK) ? 1 : 0;
624 }
625 case RETRO_DEVICE_ID_LIGHTGUN_AUX_B: {
626 Uint32 state = SDL_GetMouseState(NULL, NULL);
627 return (state & SDL_BUTTON_X1MASK) ? 1 : 0;
628 }
629 case RETRO_DEVICE_ID_LIGHTGUN_AUX_C: {
630 Uint32 state = SDL_GetMouseState(NULL, NULL);
631 return (state & SDL_BUTTON_X2MASK) ? 1 : 0;
632 }
633 case RETRO_DEVICE_ID_LIGHTGUN_START:
634 return SDL_Libretro_IsKeyDown(SDL_SCANCODE_RETURN) ? 1 : 0;
635 case RETRO_DEVICE_ID_LIGHTGUN_SELECT:
636 return SDL_Libretro_IsKeyDown(SDL_SCANCODE_RSHIFT) ? 1 : 0;
637 case RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP:
638 return SDL_Libretro_IsKeyDown(SDL_SCANCODE_UP) ? 1 : 0;
639 case RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN:
640 return SDL_Libretro_IsKeyDown(SDL_SCANCODE_DOWN) ? 1 : 0;
641 case RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT:
642 return SDL_Libretro_IsKeyDown(SDL_SCANCODE_LEFT) ? 1 : 0;
643 case RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT:
644 return SDL_Libretro_IsKeyDown(SDL_SCANCODE_RIGHT) ? 1 : 0;
645 }
646 return 0;
647 }
648
649 default:
650 return 0;
651 }
652}
653
663void SDL_Libretro_HandleEvent(SDL_Libretro* lr, const SDL_Event* event) {
664 if (!lr || !event) return;
665
666 switch (event->type) {
667 case SDL_EVENT_MOUSE_WHEEL: {
668 // Accumulate wheel deltas; SDL_Libretro_InputPoll() snapshots and
669 // clears them so each is reported for one frame.
670 lr->core.inputWheelAccumX += event->wheel.x;
671 lr->core.inputWheelAccumY += event->wheel.y;
672 break;
673 }
674 case SDL_EVENT_MOUSE_MOTION: {
675 // Accumulate relative motion, which keeps flowing at the window
676 // edges and in relative mouse mode; reported as MOUSE_X/Y.
677 lr->core.inputMotionAccumX += event->motion.xrel;
678 lr->core.inputMotionAccumY += event->motion.yrel;
679 break;
680 }
681 case SDL_EVENT_GAMEPAD_ADDED: {
682 SDL_JoystickID jid = event->gdevice.which;
683 SDL_Gamepad* gp = SDL_OpenGamepad(jid);
684 if (gp) {
685 unsigned i = 0;
686 for (; i < SDL_LIBRETRO_MAX_USERS; i++) {
687 if (!lr->gamepads[i]) {
688 lr->gamepads[i] = gp;
689 const char* name = SDL_GetGamepadName(gp);
690 if (i + 1 > lr->gamepadCount) lr->gamepadCount = i + 1;
691 SDL_Log("[SDL_Libretro] Gamepad added: #%u %s", i + 1, name ? name : "");
692 break;
693 }
694 }
695
696 // Every port is taken, so don't keep the gamepad open.
697 if (i == SDL_LIBRETRO_MAX_USERS) {
698 const char* name = SDL_GetGamepadName(gp);
699 SDL_Log("[SDL_Libretro] Gamepad ignored, no free port: %s", name ? name : "");
700 SDL_CloseGamepad(gp);
701 }
702 }
703 break;
704 }
705 case SDL_EVENT_GAMEPAD_REMOVED: {
706 SDL_JoystickID jid = event->gdevice.which;
707 for (unsigned i = 0; i < SDL_LIBRETRO_MAX_USERS; i++) {
708 if (lr->gamepads[i] && SDL_GetGamepadID(lr->gamepads[i]) == jid) {
709 const char* name = SDL_GetGamepadName(lr->gamepads[i]);
710 SDL_Log("[SDL_Libretro] Gamepad removed: #%u %s", i + 1, name ? name : "");
711 SDL_CloseGamepad(lr->gamepads[i]);
712 lr->gamepads[i] = NULL;
713 // Recompute the gamepad count as the highest occupied port + 1.
714 lr->gamepadCount = 0;
715 for (unsigned j = 0; j < SDL_LIBRETRO_MAX_USERS; j++) {
716 if (lr->gamepads[j]) lr->gamepadCount = j + 1;
717 }
718 break;
719 }
720 }
721 break;
722 }
723 case SDL_EVENT_KEY_DOWN:
724 case SDL_EVENT_KEY_UP: {
725 if (lr->core.keyboard_event) {
726 bool down = (event->type == SDL_EVENT_KEY_DOWN);
727 unsigned retrokey = SDL_Libretro_ScancodeToRetroKey(event->key.scancode);
728 uint16_t modmask = SDL_Libretro_KeymodToRetroMod(event->key.mod);
729
730 // Determine the character if one is available for the key event,
731 // applying the modifiers so Shift+1 gives '!' rather than '1'.
732 uint32_t character = 0;
733 SDL_Keycode key = SDL_GetKeyFromScancode(event->key.scancode, event->key.mod, false);
734 if (key >= 0x20 && key < 0x110000 &&
735 !(key & SDLK_SCANCODE_MASK)) {
736 character = (uint32_t)key;
737 }
738
739 lr->core.keyboard_event(down, retrokey, character, modmask);
740 }
741 break;
742 }
743 default:
744 break;
745 }
746}
747
762bool SDL_Libretro_SetPortDevice(SDL_Libretro* lr, unsigned port, unsigned device) {
763 if (!lr || port >= SDL_LIBRETRO_MAX_USERS) return false;
764 lr->core.portDeviceMap[port] = device;
765 if (SDL_Libretro_IsCoreReady(lr)) {
766 lr->core.symbols.retro_set_controller_port_device(port, device);
767 }
768 return true;
769}
770
784unsigned SDL_Libretro_GetPortDevice(const SDL_Libretro* lr, unsigned port) {
785 if (!lr || port >= SDL_LIBRETRO_MAX_USERS) return RETRO_DEVICE_NONE;
786 return lr->core.portDeviceMap[port];
787}
788
801void SDL_Libretro_SetKeyboardMapping(SDL_Libretro* lr, int retroButton, SDL_Scancode scancode) {
802 if (!lr || retroButton < 0 || retroButton > RETRO_DEVICE_ID_JOYPAD_R3) return;
803 lr->keyboardPlayer1[retroButton] = scancode;
804}
805
816void SDL_Libretro_SetGamepadMapping(SDL_Libretro* lr, unsigned port, int retroButton, SDL_GamepadButton button) {
817 if (!lr || port >= SDL_LIBRETRO_MAX_USERS ||
818 retroButton < 0 || retroButton > RETRO_DEVICE_ID_JOYPAD_R3) {
819 return;
820 }
821 lr->gamepadButtons[port][retroButton] = button;
822}
823
835void SDL_Libretro_SetAnalogToDigital(SDL_Libretro* lr, unsigned port, SDL_LibretroAnalogToDigital stick) {
836 if (!lr || port >= SDL_LIBRETRO_MAX_USERS ||
837 (int)stick < 0 || (int)stick >= SDL_LIBRETRO_ANALOG_TO_DIGITAL_COUNT) {
838 return;
839 }
840 lr->analogToDigital[port] = stick;
841}
842
853SDL_LibretroAnalogToDigital SDL_Libretro_GetAnalogToDigital(const SDL_Libretro* lr, unsigned port) {
855 return lr->analogToDigital[port];
856}
857
868void SDL_Libretro_SetMousePort(SDL_Libretro* lr, unsigned port) {
869 if (!lr || port >= SDL_LIBRETRO_MAX_USERS) return;
870 lr->mousePort = port;
871}
872
881unsigned SDL_Libretro_GetMousePort(const SDL_Libretro* lr) {
882 return lr ? lr->mousePort : 0;
883}
884
898void SDL_Libretro_SetVirtualButton(SDL_Libretro* lr, unsigned port, int button, bool pressed) {
899 if (!lr || port >= SDL_LIBRETRO_MAX_USERS ||
900 button < 0 || button >= SDL_LIBRETRO_MAX_JOYPAD_BUTTONS) {
901 return;
902 }
903 lr->core.virtualJoypadState[port][button] = pressed;
904}
905
906// Input Descriptors
907
916unsigned SDL_Libretro_GetInputDescriptorCount(const SDL_Libretro* lr) {
917 return SDL_Libretro_IsCoreReady(lr) ? lr->core.inputDescriptorsCount : 0;
918}
919
941bool SDL_Libretro_GetInputDescriptor(const SDL_Libretro* lr, unsigned index, unsigned* port, unsigned* device, unsigned* id, const char** description) {
942 if (!SDL_Libretro_IsCoreReady(lr) || index >= lr->core.inputDescriptorsCount) return false;
943 const struct retro_input_descriptor* d = &lr->core.inputDescriptors[index];
944 if (port) *port = d->port;
945 if (device) *device = d->device;
946 if (id) *id = d->id;
947 if (description) *description = d->description;
948 return true;
949}
950
951// Sensors
952
953static SDL_Sensor* SDL_Libretro_OpenSensorByType(SDL_SensorType type) {
954 int count = 0;
955 SDL_SensorID* sensors = SDL_GetSensors(&count);
956 if (!sensors) return NULL;
957 SDL_Sensor* result = NULL;
958 for (int i = 0; i < count; i++) {
959 if (SDL_GetSensorTypeForID(sensors[i]) == type) {
960 result = SDL_OpenSensor(sensors[i]);
961 break;
962 }
963 }
964 SDL_free(sensors);
965 return result;
966}
967
968static bool SDL_Libretro_SetSensorState(unsigned port, enum retro_sensor_action action, unsigned rate) {
969 SDL_Libretro* lr = SDL_Libretro_active;
970 if (!lr || port >= SDL_LIBRETRO_MAX_USERS) return false;
971 (void)rate;
972
973 switch (action) {
974 case RETRO_SENSOR_ACCELEROMETER_ENABLE:
975 if (!lr->core.sensorAccel[port]) {
976 lr->core.sensorAccel[port] = SDL_Libretro_OpenSensorByType(SDL_SENSOR_ACCEL);
977 }
978 return lr->core.sensorAccel[port] != NULL;
979 case RETRO_SENSOR_ACCELEROMETER_DISABLE:
980 if (lr->core.sensorAccel[port]) {
981 SDL_CloseSensor(lr->core.sensorAccel[port]);
982 lr->core.sensorAccel[port] = NULL;
983 }
984 SDL_memset(lr->core.sensorAccelData[port], 0, sizeof(lr->core.sensorAccelData[port]));
985 return true;
986 case RETRO_SENSOR_GYROSCOPE_ENABLE:
987 if (!lr->core.sensorGyro[port]) {
988 lr->core.sensorGyro[port] = SDL_Libretro_OpenSensorByType(SDL_SENSOR_GYRO);
989 }
990 return lr->core.sensorGyro[port] != NULL;
991 case RETRO_SENSOR_GYROSCOPE_DISABLE:
992 if (lr->core.sensorGyro[port]) {
993 SDL_CloseSensor(lr->core.sensorGyro[port]);
994 lr->core.sensorGyro[port] = NULL;
995 }
996 SDL_memset(lr->core.sensorGyroData[port], 0, sizeof(lr->core.sensorGyroData[port]));
997 return true;
998 // SDL3 does not currently support luminance sensors.
999 // case RETRO_SENSOR_ILLUMINANCE_ENABLE:
1000 // case RETRO_SENSOR_ILLUMINANCE_DISABLE:
1001 default:
1002 return false;
1003 }
1004}
1005
1006static float SDL_Libretro_GetSensorInput(unsigned port, unsigned id) {
1007 SDL_Libretro* lr = SDL_Libretro_active;
1008 if (!lr || port >= SDL_LIBRETRO_MAX_USERS) return 0.0f;
1009
1010 // Accelerometer; its ids start the list at 0.
1011 if (id <= RETRO_SENSOR_ACCELEROMETER_Z && lr->core.sensorAccel[port]) {
1012 float data[3];
1013 if (SDL_GetSensorData(lr->core.sensorAccel[port], data, 3)) {
1014 lr->core.sensorAccelData[port][0] = data[0] / SDL_STANDARD_GRAVITY;
1015 lr->core.sensorAccelData[port][1] = data[1] / SDL_STANDARD_GRAVITY;
1016 lr->core.sensorAccelData[port][2] = data[2] / SDL_STANDARD_GRAVITY;
1017 }
1018 return lr->core.sensorAccelData[port][id - RETRO_SENSOR_ACCELEROMETER_X];
1019 }
1020
1021 // Gyroscope
1022 if (id >= RETRO_SENSOR_GYROSCOPE_X && id <= RETRO_SENSOR_GYROSCOPE_Z && lr->core.sensorGyro[port]) {
1023 float data[3];
1024 if (SDL_GetSensorData(lr->core.sensorGyro[port], data, 3)) {
1025 lr->core.sensorGyroData[port][0] = data[0];
1026 lr->core.sensorGyroData[port][1] = data[1];
1027 lr->core.sensorGyroData[port][2] = data[2];
1028 }
1029 return lr->core.sensorGyroData[port][id - RETRO_SENSOR_GYROSCOPE_X];
1030 }
1031
1032 // Luminance: SDL3 does not currently support luminance sensors.
1033
1034 return 0.0f;
1035}
1036
1037static void SDL_Libretro_CloseSensors(SDL_Libretro* lr) {
1038 if (!lr) return;
1039
1040 for (unsigned i = 0; i < SDL_LIBRETRO_MAX_USERS; i++) {
1041 // Accelerometer
1042 if (lr->core.sensorAccel[i]) {
1043 SDL_CloseSensor(lr->core.sensorAccel[i]);
1044 lr->core.sensorAccel[i] = NULL;
1045 }
1046 lr->core.sensorAccelData[i][0] = 0.0f;
1047 lr->core.sensorAccelData[i][1] = 0.0f;
1048 lr->core.sensorAccelData[i][2] = 0.0f;
1049
1050 // Gyroscope
1051 if (lr->core.sensorGyro[i]) {
1052 SDL_CloseSensor(lr->core.sensorGyro[i]);
1053 lr->core.sensorGyro[i] = NULL;
1054 }
1055 lr->core.sensorGyroData[i][0] = 0.0f;
1056 lr->core.sensorGyroData[i][1] = 0.0f;
1057 lr->core.sensorGyroData[i][2] = 0.0f;
1058 }
1059}
1060
1061#endif /* SDL_LIBRETRO_INPUT_IMPL_ONCE */
#define SDL_LIBRETRO_MAX_USERS
The number of controller ports (gamepads) the frontend tracks.
bool SDL_Libretro_IsCoreReady(const SDL_Libretro *lr)
Indicates whether a core is loaded.
unsigned SDL_Libretro_GetInputDescriptorCount(const SDL_Libretro *lr)
Retrieve the number of input descriptors provided by a Libretro core.
bool SDL_Libretro_SetPortDevice(SDL_Libretro *lr, unsigned port, unsigned device)
Assign a device type to a controller port.
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.
struct SDL_Libretro SDL_Libretro
A libretro frontend context: one loaded core, its game, and the settings that carry across cores.
void SDL_Libretro_SetGamepadMapping(SDL_Libretro *lr, unsigned port, int retroButton, SDL_GamepadButton button)
Maps an SDL gamepad button to a RETRO_DEVICE_ID_JOYPAD_* button on the given controller port.
void SDL_Libretro_SetMousePort(SDL_Libretro *lr, unsigned port)
Selects the one controller port that the mouse drives.
SDL_LibretroAnalogToDigital SDL_Libretro_GetAnalogToDigital(const SDL_Libretro *lr, unsigned port)
Get the analog stick that also registers D-Pad presses on a controller port.
void SDL_Libretro_SetVirtualButton(SDL_Libretro *lr, unsigned port, int button, bool pressed)
Press or release a joypad button from code, such as from an on-screen touch control.
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.
void SDL_Libretro_SetAnalogToDigital(SDL_Libretro *lr, unsigned port, SDL_LibretroAnalogToDigital stick)
Selects which analog stick, if any, also registers D-Pad presses on the given controller port,...
SDL_LibretroAnalogToDigital
Which analog stick, if any, also registers D-Pad presses for a controller port.
unsigned SDL_Libretro_GetMousePort(const SDL_Libretro *lr)
Get the controller port that the mouse drives.
void SDL_Libretro_SetKeyboardMapping(SDL_Libretro *lr, int retroButton, SDL_Scancode scancode)
Maps a keyboard key to a RETRO_DEVICE_ID_JOYPAD_* button for Player 1 (controller port 0).
@ SDL_LIBRETRO_ANALOG_TO_DIGITAL_NONE
Only the D-Pad drives the retro joypad's directions.
@ SDL_LIBRETRO_ANALOG_TO_DIGITAL_RIGHT
The right analog stick also registers D-Pad presses.
@ SDL_LIBRETRO_ANALOG_TO_DIGITAL_COUNT
The number of possible modes.