2024-03-07 23:10:38 +05:00
|
|
|
/*
|
|
|
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
|
|
|
* running old operating systems and software designed for IBM
|
|
|
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
|
|
|
* system designs based on the PCI bus.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of the 86Box distribution.
|
|
|
|
|
*
|
|
|
|
|
* SDL2 joystick interface.
|
|
|
|
|
*
|
|
|
|
|
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
|
2025-01-10 18:42:09 -05:00
|
|
|
* Joakim L. Gilje, <jgilje@jgilje.net>
|
|
|
|
|
* Jasmine Iwanek, jriwanek@gmail.com>
|
2024-03-07 23:10:38 +05:00
|
|
|
*
|
2025-01-10 18:42:09 -05:00
|
|
|
* Copyright 2017-2021 Sarah Walker.
|
|
|
|
|
* Copyright 2021 Joakim L. Gilje.
|
|
|
|
|
* Copyright 2021-2025 Jasmine Iwanek.
|
2024-03-07 23:10:38 +05:00
|
|
|
*/
|
2021-12-13 22:45:37 +01:00
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
|
|
2024-03-07 23:10:38 +05:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#define _USE_MATH_DEFINES
|
|
|
|
|
#include <math.h>
|
|
|
|
|
/* This #undef is needed because a SDL include header redefines HAVE_STDARG_H. */
|
|
|
|
|
#undef HAVE_STDARG_H
|
|
|
|
|
#define HAVE_STDARG_H
|
|
|
|
|
#include <86box/86box.h>
|
2021-12-13 22:45:37 +01:00
|
|
|
#include <86box/device.h>
|
|
|
|
|
#include <86box/gameport.h>
|
2024-03-07 23:10:38 +05:00
|
|
|
#include <86box/plat_unused.h>
|
2021-12-13 22:45:37 +01:00
|
|
|
|
2025-07-11 16:54:03 -04:00
|
|
|
int joysticks_present = 0;
|
|
|
|
|
joystick_state_t joystick_state[GAMEPORT_MAX][MAX_JOYSTICKS];
|
|
|
|
|
plat_joystick_state_t plat_joystick_state[MAX_PLAT_JOYSTICKS];
|
|
|
|
|
static SDL_Joystick *sdl_joy[MAX_PLAT_JOYSTICKS];
|
2021-12-14 16:33:53 +06:00
|
|
|
|
2021-12-15 00:50:46 +02:00
|
|
|
#ifndef M_PI
|
2022-11-19 08:49:04 -05:00
|
|
|
# define M_PI 3.14159265358979323846
|
2021-12-15 00:50:46 +02:00
|
|
|
#endif
|
|
|
|
|
|
2022-11-19 08:49:04 -05:00
|
|
|
void
|
2024-03-07 23:10:38 +05:00
|
|
|
joystick_init(void)
|
2022-11-19 08:49:04 -05:00
|
|
|
{
|
2025-07-08 23:38:29 +02:00
|
|
|
#ifdef _WIN32
|
2024-03-07 23:10:38 +05:00
|
|
|
/* This is needed for SDL's Windows raw input backend to work properly without SDL video. */
|
|
|
|
|
SDL_SetHint(SDL_HINT_JOYSTICK_THREAD, "1");
|
2025-07-08 23:38:29 +02:00
|
|
|
#endif
|
2024-03-07 23:10:38 +05:00
|
|
|
|
2025-09-09 19:59:42 -04:00
|
|
|
#ifdef __APPLE__
|
|
|
|
|
if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) != 0)
|
|
|
|
|
#else
|
|
|
|
|
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != 0)
|
|
|
|
|
#endif
|
2021-12-13 22:45:37 +01:00
|
|
|
return;
|
2025-09-09 19:59:42 -04:00
|
|
|
|
2021-12-13 22:45:37 +01:00
|
|
|
joysticks_present = SDL_NumJoysticks();
|
|
|
|
|
|
|
|
|
|
memset(sdl_joy, 0, sizeof(sdl_joy));
|
2025-01-11 01:31:57 -05:00
|
|
|
for (int js = 0; js < joysticks_present; js++) {
|
|
|
|
|
sdl_joy[js] = SDL_JoystickOpen(js);
|
|
|
|
|
|
|
|
|
|
if (sdl_joy[js]) {
|
|
|
|
|
strncpy(plat_joystick_state[js].name, SDL_JoystickNameForIndex(js), 64);
|
|
|
|
|
plat_joystick_state[js].nr_axes = MIN(SDL_JoystickNumAxes(sdl_joy[js]), MAX_JOY_AXES);
|
|
|
|
|
plat_joystick_state[js].nr_buttons = MIN(SDL_JoystickNumButtons(sdl_joy[js]), MAX_JOY_BUTTONS);
|
|
|
|
|
plat_joystick_state[js].nr_povs = MIN(SDL_JoystickNumHats(sdl_joy[js]), MAX_JOY_POVS);
|
|
|
|
|
|
|
|
|
|
for (int axis_nr = 0; axis_nr < plat_joystick_state[js].nr_axes; axis_nr++) {
|
|
|
|
|
snprintf(plat_joystick_state[js].axis[axis_nr].name, sizeof(plat_joystick_state[js].axis[axis_nr].name), "Axis %i", axis_nr);
|
|
|
|
|
plat_joystick_state[js].axis[axis_nr].id = axis_nr;
|
2021-12-13 22:45:37 +01:00
|
|
|
}
|
2025-01-11 03:39:15 -05:00
|
|
|
for (int button_nr = 0; button_nr < plat_joystick_state[js].nr_buttons; button_nr++) {
|
|
|
|
|
snprintf(plat_joystick_state[js].button[button_nr].name, sizeof(plat_joystick_state[js].button[button_nr].name), "Button %i", button_nr);
|
|
|
|
|
plat_joystick_state[js].button[button_nr].id = button_nr;
|
2021-12-13 22:45:37 +01:00
|
|
|
}
|
2025-01-11 01:31:57 -05:00
|
|
|
for (int pov_nr = 0; pov_nr < plat_joystick_state[js].nr_povs; pov_nr++) {
|
|
|
|
|
snprintf(plat_joystick_state[js].pov[pov_nr].name, sizeof(plat_joystick_state[js].pov[pov_nr].name), "POV %i", pov_nr);
|
|
|
|
|
plat_joystick_state[js].pov[pov_nr].id = pov_nr;
|
2021-12-13 22:45:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-19 08:49:04 -05:00
|
|
|
void
|
2024-03-07 23:10:38 +05:00
|
|
|
joystick_close(void)
|
2021-12-13 22:45:37 +01:00
|
|
|
{
|
2025-01-11 01:31:57 -05:00
|
|
|
for (int js = 0; js < joysticks_present; js++) {
|
|
|
|
|
if (sdl_joy[js])
|
|
|
|
|
SDL_JoystickClose(sdl_joy[js]);
|
2021-12-13 22:45:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-19 08:49:04 -05:00
|
|
|
static int
|
2025-08-11 21:59:36 -04:00
|
|
|
joystick_get_axis(int joystick_nr, int mapping)
|
2021-12-13 22:45:37 +01:00
|
|
|
{
|
2022-11-19 08:49:04 -05:00
|
|
|
if (mapping & POV_X) {
|
|
|
|
|
switch (plat_joystick_state[joystick_nr].p[mapping & 3]) {
|
|
|
|
|
case SDL_HAT_LEFTUP:
|
|
|
|
|
case SDL_HAT_LEFT:
|
|
|
|
|
case SDL_HAT_LEFTDOWN:
|
|
|
|
|
return -32767;
|
|
|
|
|
|
|
|
|
|
case SDL_HAT_RIGHTUP:
|
|
|
|
|
case SDL_HAT_RIGHT:
|
|
|
|
|
case SDL_HAT_RIGHTDOWN:
|
|
|
|
|
return 32767;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
2021-12-13 22:45:37 +01:00
|
|
|
}
|
2022-11-19 08:49:04 -05:00
|
|
|
} else if (mapping & POV_Y) {
|
|
|
|
|
switch (plat_joystick_state[joystick_nr].p[mapping & 3]) {
|
|
|
|
|
case SDL_HAT_LEFTUP:
|
|
|
|
|
case SDL_HAT_UP:
|
|
|
|
|
case SDL_HAT_RIGHTUP:
|
|
|
|
|
return -32767;
|
|
|
|
|
|
|
|
|
|
case SDL_HAT_LEFTDOWN:
|
|
|
|
|
case SDL_HAT_DOWN:
|
|
|
|
|
case SDL_HAT_RIGHTDOWN:
|
|
|
|
|
return 32767;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
2021-12-13 22:45:37 +01:00
|
|
|
}
|
2022-11-19 08:49:04 -05:00
|
|
|
} else
|
2021-12-13 22:45:37 +01:00
|
|
|
return plat_joystick_state[joystick_nr].a[plat_joystick_state[joystick_nr].axis[mapping].id];
|
|
|
|
|
}
|
2024-03-07 23:10:38 +05:00
|
|
|
|
2022-11-19 08:49:04 -05:00
|
|
|
void
|
2025-08-11 21:59:36 -04:00
|
|
|
joystick_process(uint8_t gp)
|
2021-12-13 22:45:37 +01:00
|
|
|
{
|
2025-08-11 21:59:36 -04:00
|
|
|
if (!joystick_type[gp])
|
2022-11-19 08:49:04 -05:00
|
|
|
return;
|
2022-07-10 14:31:49 +02:00
|
|
|
|
2021-12-13 22:45:37 +01:00
|
|
|
SDL_JoystickUpdate();
|
2025-01-11 01:31:57 -05:00
|
|
|
for (int js = 0; js < joysticks_present; js++) {
|
|
|
|
|
for (int axis_nr = 0; axis_nr < plat_joystick_state[js].nr_axes; axis_nr++)
|
|
|
|
|
plat_joystick_state[js].a[axis_nr] = SDL_JoystickGetAxis(sdl_joy[js], axis_nr);
|
2021-12-13 22:45:37 +01:00
|
|
|
|
2025-01-11 01:31:57 -05:00
|
|
|
for (int button_nr = 0; button_nr < plat_joystick_state[js].nr_buttons; button_nr++)
|
|
|
|
|
plat_joystick_state[js].b[button_nr] = SDL_JoystickGetButton(sdl_joy[js], button_nr);
|
2021-12-13 22:45:37 +01:00
|
|
|
|
2025-01-11 01:31:57 -05:00
|
|
|
for (int pov_nr = 0; pov_nr < plat_joystick_state[js].nr_povs; pov_nr++)
|
|
|
|
|
plat_joystick_state[js].p[pov_nr] = SDL_JoystickGetHat(sdl_joy[js], pov_nr);
|
2021-12-13 22:45:37 +01:00
|
|
|
|
2025-01-11 01:31:57 -05:00
|
|
|
#if 0
|
2025-01-11 03:39:15 -05:00
|
|
|
pclog("joystick %i - x=%i y=%i b[0]=%i b[1]=%i %i\n", js,
|
2025-08-11 21:59:36 -04:00
|
|
|
joystick_state[gp][js].x,
|
|
|
|
|
joystick_state[gp][js].y,
|
|
|
|
|
joystick_state[gp][js].b[0],
|
|
|
|
|
joystick_state[gp][js].b[1],
|
|
|
|
|
joysticks_present[gp]);
|
2025-01-11 01:31:57 -05:00
|
|
|
#endif
|
2021-12-13 22:45:37 +01:00
|
|
|
}
|
|
|
|
|
|
2025-08-11 21:59:36 -04:00
|
|
|
for (int js = 0; js < joystick_get_max_joysticks(joystick_type[gp]); js++) {
|
|
|
|
|
if (joystick_state[gp][js].plat_joystick_nr) {
|
|
|
|
|
int joystick_nr = joystick_state[gp][js].plat_joystick_nr - 1;
|
2021-12-13 22:45:37 +01:00
|
|
|
|
2025-08-11 21:59:36 -04:00
|
|
|
for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type[gp]); axis_nr++)
|
|
|
|
|
joystick_state[gp][js].axis[axis_nr] = joystick_get_axis(joystick_nr, joystick_state[gp][js].axis_mapping[axis_nr]);
|
2021-12-13 22:45:37 +01:00
|
|
|
|
2025-08-11 21:59:36 -04:00
|
|
|
for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type[gp]); button_nr++)
|
|
|
|
|
joystick_state[gp][js].button[button_nr] = plat_joystick_state[joystick_nr].b[joystick_state[gp][js].button_mapping[button_nr]];
|
2021-12-13 22:45:37 +01:00
|
|
|
|
2025-08-11 21:59:36 -04:00
|
|
|
for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type[gp]); pov_nr++) {
|
|
|
|
|
int x = joystick_get_axis(joystick_nr, joystick_state[gp][js].pov_mapping[pov_nr][0]);
|
|
|
|
|
int y = joystick_get_axis(joystick_nr, joystick_state[gp][js].pov_mapping[pov_nr][1]);
|
2025-01-11 01:31:57 -05:00
|
|
|
double angle = (atan2((double) y, (double) x) * 360.0) / (2 * M_PI);
|
|
|
|
|
double magnitude = sqrt((double) x * (double) x + (double) y * (double) y);
|
2021-12-13 22:45:37 +01:00
|
|
|
|
|
|
|
|
if (magnitude < 16384)
|
2025-08-11 21:59:36 -04:00
|
|
|
joystick_state[gp][js].pov[pov_nr] = -1;
|
2021-12-13 22:45:37 +01:00
|
|
|
else
|
2025-08-11 21:59:36 -04:00
|
|
|
joystick_state[gp][js].pov[pov_nr] = ((int) angle + 90 + 360) % 360;
|
2021-12-13 22:45:37 +01:00
|
|
|
}
|
2022-11-19 08:49:04 -05:00
|
|
|
} else {
|
2025-08-11 21:59:36 -04:00
|
|
|
for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type[gp]); axis_nr++)
|
|
|
|
|
joystick_state[gp][js].axis[axis_nr] = 0;
|
2025-01-11 01:31:57 -05:00
|
|
|
|
2025-08-11 21:59:36 -04:00
|
|
|
for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type[gp]); button_nr++)
|
|
|
|
|
joystick_state[gp][js].button[button_nr] = 0;
|
2025-01-11 01:31:57 -05:00
|
|
|
|
2025-08-11 21:59:36 -04:00
|
|
|
for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type[gp]); pov_nr++)
|
|
|
|
|
joystick_state[gp][js].pov[pov_nr] = -1;
|
2021-12-13 22:45:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-07 23:10:38 +05:00
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
void
|
|
|
|
|
win_joystick_handle(UNUSED(void *raw))
|
|
|
|
|
{
|
|
|
|
|
/* Nothing to be done here, atleast currently */
|
|
|
|
|
}
|
|
|
|
|
#endif
|