From efbbafb3f15afe6e9538431937428dd6a489c1ea Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 10 Mar 2024 21:06:14 -0700 Subject: [PATCH] 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!) --- build-scripts/SDL_migration.cocci | 14 +++++ docs/README-migration.md | 4 +- include/SDL3/SDL_events.h | 24 ++++++- include/SDL3/SDL_joystick.h | 51 ++++++++++++--- include/SDL3/SDL_oldnames.h | 6 ++ src/dynapi/SDL_dynapi.sym | 2 + src/dynapi/SDL_dynapi_overrides.h | 2 + src/dynapi/SDL_dynapi_procs.h | 2 + src/events/SDL_events.c | 6 ++ src/joystick/SDL_joystick.c | 86 ++++++++++++++++++++++++-- src/joystick/SDL_joystick_c.h | 19 +++--- src/joystick/SDL_sysjoystick.h | 10 +++ src/joystick/linux/SDL_sysjoystick.c | 52 ++++++++++++++++ src/joystick/linux/SDL_sysjoystick_c.h | 6 ++ src/test/SDL_test_common.c | 5 ++ test/testautomation_joystick.c | 1 + 16 files changed, 262 insertions(+), 28 deletions(-) diff --git a/build-scripts/SDL_migration.cocci b/build-scripts/SDL_migration.cocci index 7cde786bc..989b4c9eb 100644 --- a/build-scripts/SDL_migration.cocci +++ b/build-scripts/SDL_migration.cocci @@ -2153,6 +2153,10 @@ expression e; + SDL_EVENT_JOYSTICK_AXIS_MOTION @@ @@ +- SDL_JOYBALLMOTION ++ SDL_EVENT_JOYSTICK_BALL_MOTION +@@ +@@ - SDL_JOYHATMOTION + SDL_EVENT_JOYSTICK_HAT_MOTION @@ @@ -3044,3 +3048,13 @@ typedef SDL_version, SDL_Version; @@ - SDL_HINT_PS2_DYNAMIC_VSYNC + SDL_HINT_RENDER_PS2_DYNAMIC_VSYNC +@@ +@@ +- SDL_JoystickNumBalls ++ SDL_NumJoystickBalls + (...) +@@ +@@ +- SDL_JoystickGetBall ++ SDL_GetJoystickBall + (...) diff --git a/docs/README-migration.md b/docs/README-migration.md index 18dd8decf..c5f279c8f 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -370,6 +370,7 @@ The following symbols have been renamed: * SDL_FINGERUP => SDL_EVENT_FINGER_UP * SDL_FIRSTEVENT => SDL_EVENT_FIRST * SDL_JOYAXISMOTION => SDL_EVENT_JOYSTICK_AXIS_MOTION +* SDL_JOYBALLMOTION => SDL_EVENT_JOYSTICK_BALL_MOTION * SDL_JOYBATTERYUPDATED => SDL_EVENT_JOYSTICK_BATTERY_UPDATED * SDL_JOYBUTTONDOWN => SDL_EVENT_JOYSTICK_BUTTON_DOWN * SDL_JOYBUTTONUP => SDL_EVENT_JOYSTICK_BUTTON_UP @@ -797,6 +798,7 @@ The following functions have been renamed: * SDL_JoystickGetAttached() => SDL_JoystickConnected() * SDL_JoystickGetAxis() => SDL_GetJoystickAxis() * SDL_JoystickGetAxisInitialState() => SDL_GetJoystickAxisInitialState() +* SDL_JoystickGetBall() => SDL_GetJoystickBall() * SDL_JoystickGetButton() => SDL_GetJoystickButton() * SDL_JoystickGetFirmwareVersion() => SDL_GetJoystickFirmwareVersion() * SDL_JoystickGetGUID() => SDL_GetJoystickGUID() @@ -813,6 +815,7 @@ The following functions have been renamed: * SDL_JoystickIsVirtual() => SDL_IsJoystickVirtual() * SDL_JoystickName() => SDL_GetJoystickName() * SDL_JoystickNumAxes() => SDL_GetNumJoystickAxes() +* SDL_JoystickNumBalls() => SDL_GetNumJoystickBalls() * SDL_JoystickNumButtons() => SDL_GetNumJoystickButtons() * SDL_JoystickNumHats() => SDL_GetNumJoystickHats() * SDL_JoystickOpen() => SDL_OpenJoystick() @@ -843,7 +846,6 @@ The following functions have been removed: * SDL_JoystickHasRumble() - replaced with SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN * SDL_JoystickHasRumbleTriggers() - replaced with SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN * SDL_JoystickNameForIndex() - replaced with SDL_GetJoystickInstanceName() -* SDL_JoystickNumBalls() - API has been removed, see https://github.com/libsdl-org/SDL/issues/6766 * SDL_JoystickPathForIndex() - replaced with SDL_GetJoystickInstancePath() * SDL_NumJoysticks() - replaced with SDL_GetJoysticks() diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index ec7002e56..2f1a7d5af 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -152,11 +152,12 @@ typedef enum /* Joystick events */ SDL_EVENT_JOYSTICK_AXIS_MOTION = 0x600, /**< Joystick axis motion */ - SDL_EVENT_JOYSTICK_HAT_MOTION = 0x602, /**< Joystick hat position change */ + SDL_EVENT_JOYSTICK_BALL_MOTION, /**< Joystick trackball motion */ + SDL_EVENT_JOYSTICK_HAT_MOTION, /**< Joystick hat position change */ SDL_EVENT_JOYSTICK_BUTTON_DOWN, /**< Joystick button pressed */ SDL_EVENT_JOYSTICK_BUTTON_UP, /**< Joystick button released */ - SDL_EVENT_JOYSTICK_ADDED, /**< A new joystick has been inserted into the system */ - SDL_EVENT_JOYSTICK_REMOVED, /**< An opened joystick has been removed */ + SDL_EVENT_JOYSTICK_ADDED, /**< A new joystick has been inserted into the system */ + SDL_EVENT_JOYSTICK_REMOVED, /**< An opened joystick has been removed */ SDL_EVENT_JOYSTICK_BATTERY_UPDATED, /**< Joystick battery level change */ SDL_EVENT_JOYSTICK_UPDATE_COMPLETE, /**< Joystick update is complete */ @@ -336,6 +337,22 @@ typedef struct SDL_MouseMotionEvent float yrel; /**< The relative motion in the Y direction */ } SDL_MouseMotionEvent; +/** + * \brief Joystick trackball motion event structure (event.jball.*) + */ +typedef struct SDL_JoyBallEvent +{ + Uint32 type; /**< ::SDL_JOYBALLMOTION */ + Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ + SDL_JoystickID which; /**< The joystick instance id */ + Uint8 ball; /**< The joystick trackball index */ + Uint8 padding1; + Uint8 padding2; + Uint8 padding3; + Sint16 xrel; /**< The relative motion in the X direction */ + Sint16 yrel; /**< The relative motion in the Y direction */ +} SDL_JoyBallEvent; + /** * Mouse button event structure (event.button.*) */ @@ -706,6 +723,7 @@ typedef union SDL_Event SDL_MouseButtonEvent button; /**< Mouse button event data */ SDL_MouseWheelEvent wheel; /**< Mouse wheel event data */ SDL_JoyAxisEvent jaxis; /**< Joystick axis event data */ + SDL_JoyBallEvent jball; /**< Joystick ball event data */ SDL_JoyHatEvent jhat; /**< Joystick hat event data */ SDL_JoyButtonEvent jbutton; /**< Joystick button event data */ SDL_JoyDeviceEvent jdevice; /**< Joystick device change event data */ diff --git a/include/SDL3/SDL_joystick.h b/include/SDL3/SDL_joystick.h index 6c3fc2024..4d45467b3 100644 --- a/include/SDL3/SDL_joystick.h +++ b/include/SDL3/SDL_joystick.h @@ -732,6 +732,24 @@ extern DECLSPEC SDL_JoystickID SDLCALL SDL_GetJoystickInstanceID(SDL_Joystick *j */ extern DECLSPEC int SDLCALL SDL_GetNumJoystickAxes(SDL_Joystick *joystick); +/** + * Get the number of trackballs on a joystick. + * + * Joystick trackballs have only relative motion events associated with them + * and their state cannot be polled. + * + * Most joysticks do not have trackballs. + * + * \param joystick an SDL_Joystick structure containing joystick information + * \returns the number of trackballs on success or a negative error code on + * failure; call SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetJoystickBall + */ +extern DECLSPEC int SDLCALL SDL_GetNumJoystickBalls(SDL_Joystick *joystick); + /** * Get the number of POV hats on a joystick. * @@ -823,8 +841,7 @@ extern DECLSPEC void SDLCALL SDL_UpdateJoysticks(void); * * \sa SDL_GetNumJoystickAxes */ -extern DECLSPEC Sint16 SDLCALL SDL_GetJoystickAxis(SDL_Joystick *joystick, - int axis); +extern DECLSPEC Sint16 SDLCALL SDL_GetJoystickAxis(SDL_Joystick *joystick, int axis); /** * Get the initial state of an axis control on a joystick. @@ -840,8 +857,28 @@ extern DECLSPEC Sint16 SDLCALL SDL_GetJoystickAxis(SDL_Joystick *joystick, * * \since This function is available since SDL 3.0.0. */ -extern DECLSPEC SDL_bool SDLCALL SDL_GetJoystickAxisInitialState(SDL_Joystick *joystick, - int axis, Sint16 *state); +extern DECLSPEC SDL_bool SDLCALL SDL_GetJoystickAxisInitialState(SDL_Joystick *joystick, int axis, Sint16 *state); + +/** + * Get the ball axis change since the last poll. + * + * Trackballs can only return relative motion since the last call to + * SDL_GetJoystickBall(), these motion deltas are placed into `dx` and `dy`. + * + * Most joysticks do not have trackballs. + * + * \param joystick the SDL_Joystick to query + * \param ball the ball index to query; ball indices start at index 0 + * \param dx stores the difference in the x axis position since the last poll + * \param dy stores the difference in the y axis position since the last poll + * \returns 0 on success or a negative error code on failure; call + * SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetNumJoystickBalls + */ +extern DECLSPEC int SDLCALL SDL_GetJoystickBall(SDL_Joystick *joystick, int ball, int *dx, int *dy); /** * \name Hat positions @@ -881,8 +918,7 @@ extern DECLSPEC SDL_bool SDLCALL SDL_GetJoystickAxisInitialState(SDL_Joystick *j * * \sa SDL_GetNumJoystickHats */ -extern DECLSPEC Uint8 SDLCALL SDL_GetJoystickHat(SDL_Joystick *joystick, - int hat); +extern DECLSPEC Uint8 SDLCALL SDL_GetJoystickHat(SDL_Joystick *joystick, int hat); /** * Get the current state of a button on a joystick. @@ -896,8 +932,7 @@ extern DECLSPEC Uint8 SDLCALL SDL_GetJoystickHat(SDL_Joystick *joystick, * * \sa SDL_GetNumJoystickButtons */ -extern DECLSPEC Uint8 SDLCALL SDL_GetJoystickButton(SDL_Joystick *joystick, - int button); +extern DECLSPEC Uint8 SDLCALL SDL_GetJoystickButton(SDL_Joystick *joystick, int button); /** * Start a rumble effect. diff --git a/include/SDL3/SDL_oldnames.h b/include/SDL3/SDL_oldnames.h index 0dafc18d8..8ca7dddd0 100644 --- a/include/SDL3/SDL_oldnames.h +++ b/include/SDL3/SDL_oldnames.h @@ -115,6 +115,7 @@ #define SDL_JOYBUTTONUP SDL_EVENT_JOYSTICK_BUTTON_UP #define SDL_JOYDEVICEADDED SDL_EVENT_JOYSTICK_ADDED #define SDL_JOYDEVICEREMOVED SDL_EVENT_JOYSTICK_REMOVED +#define SDL_JOYBALLMOTION SDL_EVENT_JOYSTICK_BALL_MOTION #define SDL_JOYHATMOTION SDL_EVENT_JOYSTICK_HAT_MOTION #define SDL_KEYDOWN SDL_EVENT_KEY_DOWN #define SDL_KEYMAPCHANGED SDL_EVENT_KEYMAP_CHANGED @@ -302,6 +303,7 @@ #define SDL_JoystickGetAttached SDL_JoystickConnected #define SDL_JoystickGetAxis SDL_GetJoystickAxis #define SDL_JoystickGetAxisInitialState SDL_GetJoystickAxisInitialState +#define SDL_JoystickGetBall SDL_GetJoystickBall #define SDL_JoystickGetButton SDL_GetJoystickButton #define SDL_JoystickGetFirmwareVersion SDL_GetJoystickFirmwareVersion #define SDL_JoystickGetGUID SDL_GetJoystickGUID @@ -318,6 +320,7 @@ #define SDL_JoystickIsVirtual SDL_IsJoystickVirtual #define SDL_JoystickName SDL_GetJoystickName #define SDL_JoystickNumAxes SDL_GetNumJoystickAxes +#define SDL_JoystickNumBalls SDL_GetNumJoystickBalls #define SDL_JoystickNumButtons SDL_GetNumJoystickButtons #define SDL_JoystickNumHats SDL_GetNumJoystickHats #define SDL_JoystickOpen SDL_OpenJoystick @@ -597,6 +600,7 @@ #define SDL_JOYBUTTONUP SDL_JOYBUTTONUP_renamed_SDL_EVENT_JOYSTICK_BUTTON_UP #define SDL_JOYDEVICEADDED SDL_JOYDEVICEADDED_renamed_SDL_EVENT_JOYSTICK_ADDED #define SDL_JOYDEVICEREMOVED SDL_JOYDEVICEREMOVED_renamed_SDL_EVENT_JOYSTICK_REMOVED +#define SDL_JOYBALLMOTION SDL_JOYBALLMOTION_renamed_SDL_EVENT_JOYSTICK_BALL_MOTION #define SDL_JOYHATMOTION SDL_JOYHATMOTION_renamed_SDL_EVENT_JOYSTICK_HAT_MOTION #define SDL_KEYDOWN SDL_KEYDOWN_renamed_SDL_EVENT_KEY_DOWN #define SDL_KEYMAPCHANGED SDL_KEYMAPCHANGED_renamed_SDL_EVENT_KEYMAP_CHANGED @@ -785,6 +789,7 @@ #define SDL_JoystickGetAttached SDL_JoystickGetAttached_renamed_SDL_JoystickConnected #define SDL_JoystickGetAxis SDL_JoystickGetAxis_renamed_SDL_GetJoystickAxis #define SDL_JoystickGetAxisInitialState SDL_JoystickGetAxisInitialState_renamed_SDL_GetJoystickAxisInitialState +#define SDL_JoystickGetBall SDL_JoystickGetBall_renamed_SDL_GetJoystickBall #define SDL_JoystickGetButton SDL_JoystickGetButton_renamed_SDL_GetJoystickButton #define SDL_JoystickGetFirmwareVersion SDL_JoystickGetFirmwareVersion_renamed_SDL_GetJoystickFirmwareVersion #define SDL_JoystickGetGUID SDL_JoystickGetGUID_renamed_SDL_GetJoystickGUID @@ -801,6 +806,7 @@ #define SDL_JoystickIsVirtual SDL_JoystickIsVirtual_renamed_SDL_IsJoystickVirtual #define SDL_JoystickName SDL_JoystickName_renamed_SDL_GetJoystickName #define SDL_JoystickNumAxes SDL_JoystickNumAxes_renamed_SDL_GetNumJoystickAxes +#define SDL_JoystickNumBalls SDL_JoystickNumBalls_renamed_SDL_GetNumJoystickBalls #define SDL_JoystickNumButtons SDL_JoystickNumButtons_renamed_SDL_GetNumJoystickButtons #define SDL_JoystickNumHats SDL_JoystickNumHats_renamed_SDL_GetNumJoystickHats #define SDL_JoystickOpen SDL_JoystickOpen_renamed_SDL_OpenJoystick diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 5d08979e0..67dec60c3 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -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: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index c5378f1fd..5ee503eb9 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -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 diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 0c84f92d9..77494c653 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -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) diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 01d126350..102ae5245 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -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, diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index f68968ff0..82fb8a799 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -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, diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index 0f00d180b..fd50467cb 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -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); diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index d3d8e71de..68eb038e0 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -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 */ diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index bf1304971..5de8b7581 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -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); } diff --git a/src/joystick/linux/SDL_sysjoystick_c.h b/src/joystick/linux/SDL_sysjoystick_c.h index 98bee2042..8e441c0a9 100644 --- a/src/joystick/linux/SDL_sysjoystick_c.h +++ b/src/joystick/linux/SDL_sysjoystick_c.h @@ -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 { diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 952ac60bc..c447e14f1 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -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"; diff --git a/test/testautomation_joystick.c b/test/testautomation_joystick.c index 4f191047e..b78770fee 100644 --- a/test/testautomation_joystick.c +++ b/test/testautomation_joystick.c @@ -50,6 +50,7 @@ static int TestVirtualJoystick(void *arg) SDLTest_AssertCheck(SDL_GetJoystickSerial(joystick) == NULL, "SDL_GetJoystickSerial()"); SDLTest_AssertCheck(SDL_GetJoystickType(joystick) == desc.type, "SDL_GetJoystickType()"); SDLTest_AssertCheck(SDL_GetNumJoystickAxes(joystick) == desc.naxes, "SDL_GetNumJoystickAxes()"); + SDLTest_AssertCheck(SDL_GetNumJoystickBalls(joystick) == 0, "SDL_GetNumJoystickBalls()"); SDLTest_AssertCheck(SDL_GetNumJoystickHats(joystick) == desc.nhats, "SDL_GetNumJoystickHats()"); SDLTest_AssertCheck(SDL_GetNumJoystickButtons(joystick) == desc.nbuttons, "SDL_GetNumJoystickButtons()");