Re-added balls to the SDL joystick API

It turns out these were being used on Linux and at least one virtual driver was making use of them (thanks @mrfixit2001!)
This commit is contained in:
Sam Lantinga
2024-03-10 21:06:14 -07:00
parent 53e86be22f
commit efbbafb3f1
16 changed files with 262 additions and 28 deletions

View File

@@ -974,6 +974,8 @@ SDL3_0.0.0 {
SDL_qsort_r;
SDL_bsearch_r;
SDL_AddVulkanRenderSemaphores;
SDL_GetNumJoystickBalls;
SDL_GetJoystickBall;
# extra symbols go here (don't modify this line)
local: *;
};

View File

@@ -999,3 +999,5 @@
#define SDL_qsort_r SDL_qsort_r_REAL
#define SDL_bsearch_r SDL_bsearch_r_REAL
#define SDL_AddVulkanRenderSemaphores SDL_AddVulkanRenderSemaphores_REAL
#define SDL_GetNumJoystickBalls SDL_GetNumJoystickBalls_REAL
#define SDL_GetJoystickBall SDL_GetJoystickBall_REAL

View File

@@ -1024,3 +1024,5 @@ SDL_DYNAPI_PROC(SDL_CameraPosition,SDL_GetCameraDevicePosition,(SDL_CameraDevice
SDL_DYNAPI_PROC(void,SDL_qsort_r,(void *a, size_t b, size_t c, int (SDLCALL *d)(void *, const void *, const void *), void *e),(a,b,c,d,e),)
SDL_DYNAPI_PROC(void*,SDL_bsearch_r,(const void *a, const void *b, size_t c, size_t d, int (SDLCALL *e)(void *, const void *, const void *), void *f),(a,b,c,d,e,f),return)
SDL_DYNAPI_PROC(int,SDL_AddVulkanRenderSemaphores,(SDL_Renderer *a, Uint32 b, Sint64 c, Sint64 d),(a,b,c,d),return)
SDL_DYNAPI_PROC(int,SDL_GetNumJoystickBalls,(SDL_Joystick *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetJoystickBall,(SDL_Joystick *a, int b, int *c, int *d),(a,b,c,d),return)

View File

@@ -383,6 +383,12 @@ static void SDL_LogEvent(const SDL_Event *event)
(uint)event->jaxis.axis, (int)event->jaxis.value);
break;
SDL_EVENT_CASE(SDL_EVENT_JOYSTICK_BALL_MOTION)
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d ball=%u xrel=%d yrel=%d)",
(uint)event->jball.timestamp, (int)event->jball.which,
(uint)event->jball.ball, (int)event->jball.xrel, (int)event->jball.yrel);
break;
SDL_EVENT_CASE(SDL_EVENT_JOYSTICK_HAT_MOTION)
(void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d hat=%u value=%u)",
(uint)event->jhat.timestamp, (int)event->jhat.which,

View File

@@ -1091,15 +1091,21 @@ SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)
joystick->guid = driver->GetDeviceGUID(device_index);
if (joystick->naxes > 0) {
joystick->axes = (SDL_JoystickAxisInfo *)SDL_calloc(joystick->naxes, sizeof(SDL_JoystickAxisInfo));
joystick->axes = (SDL_JoystickAxisInfo *)SDL_calloc(joystick->naxes, sizeof(*joystick->axes));
}
if (joystick->nballs > 0) {
joystick->balls = (SDL_JoystickBallData *)SDL_calloc(joystick->nballs, sizeof(*joystick->balls));
}
if (joystick->nhats > 0) {
joystick->hats = (Uint8 *)SDL_calloc(joystick->nhats, sizeof(Uint8));
joystick->hats = (Uint8 *)SDL_calloc(joystick->nhats, sizeof(*joystick->hats));
}
if (joystick->nbuttons > 0) {
joystick->buttons = (Uint8 *)SDL_calloc(joystick->nbuttons, sizeof(Uint8));
joystick->buttons = (Uint8 *)SDL_calloc(joystick->nbuttons, sizeof(*joystick->buttons));
}
if (((joystick->naxes > 0) && !joystick->axes) || ((joystick->nhats > 0) && !joystick->hats) || ((joystick->nbuttons > 0) && !joystick->buttons)) {
if (((joystick->naxes > 0) && !joystick->axes) ||
((joystick->nballs > 0) && !joystick->balls) ||
((joystick->nhats > 0) && !joystick->hats) ||
((joystick->nbuttons > 0) && !joystick->buttons)) {
SDL_CloseJoystick(joystick);
SDL_UnlockJoysticks();
return NULL;
@@ -1324,6 +1330,16 @@ int SDL_GetNumJoystickHats(SDL_Joystick *joystick)
return retval;
}
/*
* Get the number of trackballs on a joystick
*/
int SDL_GetNumJoystickBalls(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
return joystick->nballs;
}
/*
* Get the number of buttons on a joystick
*/
@@ -1414,6 +1430,31 @@ Uint8 SDL_GetJoystickHat(SDL_Joystick *joystick, int hat)
return state;
}
/*
* Get the ball axis change since the last poll
*/
int SDL_GetJoystickBall(SDL_Joystick *joystick, int ball, int *dx, int *dy)
{
int retval;
CHECK_JOYSTICK_MAGIC(joystick, -1);
retval = 0;
if (ball < joystick->nballs) {
if (dx) {
*dx = joystick->balls[ball].dx;
}
if (dy) {
*dy = joystick->balls[ball].dy;
}
joystick->balls[ball].dx = 0;
joystick->balls[ball].dy = 0;
} else {
return SDL_SetError("Joystick only has %d balls", joystick->nballs);
}
return retval;
}
/*
* Get the current state of a button on a joystick
*/
@@ -1781,6 +1822,7 @@ void SDL_CloseJoystick(SDL_Joystick *joystick)
SDL_free(joystick->path);
SDL_free(joystick->serial);
SDL_free(joystick->axes);
SDL_free(joystick->balls);
SDL_free(joystick->hats);
SDL_free(joystick->buttons);
for (i = 0; i < joystick->ntouchpads; i++) {
@@ -2101,6 +2143,41 @@ int SDL_SendJoystickAxis(Uint64 timestamp, SDL_Joystick *joystick, Uint8 axis, S
return posted;
}
int SDL_SendJoystickBall(Uint64 timestamp, SDL_Joystick *joystick, Uint8 ball, Sint16 xrel, Sint16 yrel)
{
int posted;
SDL_AssertJoysticksLocked();
/* Make sure we're not getting garbage events */
if (ball >= joystick->nballs) {
return 0;
}
/* We ignore events if we don't have keyboard focus. */
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
return 0;
}
/* Update internal mouse state */
joystick->balls[ball].dx += xrel;
joystick->balls[ball].dy += yrel;
/* Post the event, if desired */
posted = 0;
if (SDL_EventEnabled(SDL_EVENT_JOYSTICK_BALL_MOTION)) {
SDL_Event event;
event.type = SDL_EVENT_JOYSTICK_BALL_MOTION;
event.common.timestamp = timestamp;
event.jball.which = joystick->instance_id;
event.jball.ball = ball;
event.jball.xrel = xrel;
event.jball.yrel = yrel;
posted = SDL_PushEvent(&event) == 1;
}
return posted;
}
int SDL_SendJoystickHat(Uint64 timestamp, SDL_Joystick *joystick, Uint8 hat, Uint8 value)
{
int posted;
@@ -2310,6 +2387,7 @@ void SDL_UpdateJoysticks(void)
static const Uint32 SDL_joystick_event_list[] = {
SDL_EVENT_JOYSTICK_AXIS_MOTION,
SDL_EVENT_JOYSTICK_BALL_MOTION,
SDL_EVENT_JOYSTICK_HAT_MOTION,
SDL_EVENT_JOYSTICK_BUTTON_DOWN,
SDL_EVENT_JOYSTICK_BUTTON_UP,

View File

@@ -161,18 +161,13 @@ extern void SDL_PrivateJoystickAdded(SDL_JoystickID instance_id);
extern SDL_bool SDL_IsJoystickBeingAdded(void);
extern void SDL_PrivateJoystickRemoved(SDL_JoystickID instance_id);
extern void SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick);
extern int SDL_SendJoystickAxis(Uint64 timestamp, SDL_Joystick *joystick,
Uint8 axis, Sint16 value);
extern int SDL_SendJoystickHat(Uint64 timestamp, SDL_Joystick *joystick,
Uint8 hat, Uint8 value);
extern int SDL_SendJoystickButton(Uint64 timestamp, SDL_Joystick *joystick,
Uint8 button, Uint8 state);
extern int SDL_SendJoystickTouchpad(Uint64 timestamp, SDL_Joystick *joystick,
int touchpad, int finger, Uint8 state, float x, float y, float pressure);
extern int SDL_SendJoystickSensor(Uint64 timestamp, SDL_Joystick *joystick,
SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values);
extern void SDL_SendJoystickBatteryLevel(SDL_Joystick *joystick,
SDL_JoystickPowerLevel ePowerLevel);
extern int SDL_SendJoystickAxis(Uint64 timestamp, SDL_Joystick *joystick, Uint8 axis, Sint16 value);
extern int SDL_SendJoystickBall(Uint64 timestamp, SDL_Joystick *joystick, Uint8 ball, Sint16 xrel, Sint16 yrel);
extern int SDL_SendJoystickHat(Uint64 timestamp, SDL_Joystick *joystick, Uint8 hat, Uint8 value);
extern int SDL_SendJoystickButton(Uint64 timestamp, SDL_Joystick *joystick, Uint8 button, Uint8 state);
extern int SDL_SendJoystickTouchpad(Uint64 timestamp, SDL_Joystick *joystick, int touchpad, int finger, Uint8 state, float x, float y, float pressure);
extern int SDL_SendJoystickSensor(Uint64 timestamp, SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values);
extern void SDL_SendJoystickBatteryLevel(SDL_Joystick *joystick, SDL_JoystickPowerLevel ePowerLevel);
/* Function to get the Steam virtual gamepad info for a joystick */
extern const struct SDL_SteamVirtualGamepadInfo *SDL_GetJoystickInstanceVirtualGamepadInfo(SDL_JoystickID instance_id);

View File

@@ -32,6 +32,7 @@ extern "C" {
#endif
/* The SDL joystick structure */
typedef struct SDL_JoystickAxisInfo
{
Sint16 initial_value; /* Initial axis state */
@@ -43,6 +44,12 @@ typedef struct SDL_JoystickAxisInfo
SDL_bool sending_initial_value; /* Whether we are sending the initial axis value */
} SDL_JoystickAxisInfo;
typedef struct SDL_JoystickBallData
{
int dx;
int dy;
} SDL_JoystickBallData;
typedef struct SDL_JoystickTouchpadFingerInfo
{
Uint8 state;
@@ -82,6 +89,9 @@ struct SDL_Joystick
int naxes _guarded; /* Number of axis controls on the joystick */
SDL_JoystickAxisInfo *axes _guarded;
int nballs _guarded; /* Number of trackballs on the joystick */
SDL_JoystickBallData *balls _guarded; /* Current ball motion deltas */
int nhats _guarded; /* Number of hats on the joystick */
Uint8 *hats _guarded; /* Current hat states */

View File

@@ -1147,6 +1147,16 @@ static SDL_JoystickID LINUX_JoystickGetDeviceInstanceID(int device_index)
return GetJoystickByDevIndex(device_index)->device_instance;
}
static int allocate_balldata(SDL_Joystick *joystick)
{
joystick->hwdata->balls =
(struct hwdata_ball *)SDL_calloc(joystick->nballs, sizeof(struct hwdata_ball));
if (joystick->hwdata->balls == NULL) {
return -1;
}
return 0;
}
static int allocate_hatdata(SDL_Joystick *joystick)
{
int i;
@@ -1319,6 +1329,9 @@ static void ConfigJoystick(SDL_Joystick *joystick, int fd, int fd_sensor)
++joystick->naxes;
}
}
if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {
++joystick->nballs;
}
} else if ((ioctl(fd, JSIOCGBUTTONS, &key_pam_size, sizeof(key_pam_size)) >= 0) &&
(ioctl(fd, JSIOCGAXES, &abs_pam_size, sizeof(abs_pam_size)) >= 0)) {
@@ -1426,6 +1439,11 @@ static void ConfigJoystick(SDL_Joystick *joystick, int fd, int fd_sensor)
}
/* Allocate data to keep track of these thingamajigs */
if (joystick->nballs > 0) {
if (allocate_balldata(joystick) < 0) {
joystick->nballs = 0;
}
}
if (joystick->nhats > 0) {
if (allocate_hatdata(joystick) < 0) {
joystick->nhats = 0;
@@ -1754,6 +1772,11 @@ static void HandleHat(Uint64 timestamp, SDL_Joystick *stick, int hatidx, int axi
}
}
static void HandleBall(SDL_Joystick *stick, Uint8 ball, int axis, int value)
{
stick->hwdata->balls[ball].axis[axis] += value;
}
static int AxisCorrect(SDL_Joystick *joystick, int which, int value)
{
struct axis_correct *correct;
@@ -1844,6 +1867,8 @@ static void PollAllValues(Uint64 timestamp, SDL_Joystick *joystick)
}
}
}
/* Joyballs are relative input, so there's no poll state. Events only! */
}
static void PollAllSensors(Uint64 timestamp, SDL_Joystick *joystick)
@@ -1952,6 +1977,17 @@ static void HandleInputEvents(SDL_Joystick *joystick)
break;
}
break;
case EV_REL:
switch (code) {
case REL_X:
case REL_Y:
code -= REL_X;
HandleBall(joystick, code / 2, code % 2, event->value);
break;
default:
break;
}
break;
case EV_SYN:
switch (code) {
case SYN_DROPPED:
@@ -2121,6 +2157,8 @@ static void HandleClassicEvents(SDL_Joystick *joystick)
static void LINUX_JoystickUpdate(SDL_Joystick *joystick)
{
int i;
SDL_AssertJoysticksLocked();
if (joystick->hwdata->m_bSteamController) {
@@ -2133,6 +2171,19 @@ static void LINUX_JoystickUpdate(SDL_Joystick *joystick)
} else {
HandleInputEvents(joystick);
}
/* Deliver ball motion updates */
for (i = 0; i < joystick->nballs; ++i) {
int xrel, yrel;
xrel = joystick->hwdata->balls[i].axis[0];
yrel = joystick->hwdata->balls[i].axis[1];
if (xrel || yrel) {
joystick->hwdata->balls[i].axis[0] = 0;
joystick->hwdata->balls[i].axis[1] = 0;
SDL_SendJoystickBall(0, joystick, (Uint8)i, xrel, yrel);
}
}
}
/* Function to close a joystick after use */
@@ -2160,6 +2211,7 @@ static void LINUX_JoystickClose(SDL_Joystick *joystick)
SDL_free(joystick->hwdata->key_pam);
SDL_free(joystick->hwdata->abs_pam);
SDL_free(joystick->hwdata->hats);
SDL_free(joystick->hwdata->balls);
SDL_free(joystick->hwdata->fname);
SDL_free(joystick->hwdata);
}

View File

@@ -43,6 +43,12 @@ struct joystick_hwdata
struct ff_effect effect;
Uint32 effect_expiration;
/* The current Linux joystick driver maps balls to two axes */
struct hwdata_ball
{
int axis[2];
} *balls;
/* The current Linux joystick driver maps hats to two axes */
struct hwdata_hat
{

View File

@@ -1719,6 +1719,11 @@ static void SDLTest_PrintEvent(const SDL_Event *event)
SDL_Log("SDL EVENT: Joystick %" SDL_PRIu32 " removed",
event->jdevice.which);
break;
case SDL_EVENT_JOYSTICK_BALL_MOTION:
SDL_Log("SDL EVENT: Joystick %" SDL_PRIs32 ": ball %d moved by %d,%d",
event->jball.which, event->jball.ball, event->jball.xrel,
event->jball.yrel);
break;
case SDL_EVENT_JOYSTICK_HAT_MOTION:
{
const char *position = "UNKNOWN";