initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

788
thirdparty/sdl/haptic/SDL_haptic.c vendored Normal file
View File

@@ -0,0 +1,788 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_syshaptic.h"
#include "SDL_haptic_c.h"
#include "../joystick/SDL_joystick_c.h" // For SDL_IsJoystickValid
#include "../SDL_hints_c.h"
typedef struct SDL_Haptic_VIDPID_Naxes {
Uint16 vid;
Uint16 pid;
Uint16 naxes;
} SDL_Haptic_VIDPID_Naxes;
static void SDL_Haptic_Load_Axes_List(SDL_Haptic_VIDPID_Naxes **entries, int *num_entries)
{
SDL_Haptic_VIDPID_Naxes entry;
const char *spot;
int length = 0;
spot = SDL_GetHint(SDL_HINT_JOYSTICK_HAPTIC_AXES);
if (!spot)
return;
while (SDL_sscanf(spot, "0x%hx/0x%hx/%hu%n", &entry.vid, &entry.pid, &entry.naxes, &length) == 3) {
SDL_assert(length > 0);
spot += length;
length = 0;
if ((*num_entries % 8) == 0) {
int new_max = *num_entries + 8;
SDL_Haptic_VIDPID_Naxes *new_entries =
(SDL_Haptic_VIDPID_Naxes *)SDL_realloc(*entries, new_max * sizeof(**entries));
// Out of memory, go with what we have already
if (!new_entries)
break;
*entries = new_entries;
}
(*entries)[(*num_entries)++] = entry;
if (spot[0] == ',')
spot++;
}
}
// /* Return -1 if not found */
static int SDL_Haptic_Naxes_List_Index(struct SDL_Haptic_VIDPID_Naxes *entries, int num_entries, Uint16 vid, Uint16 pid)
{
if (!entries)
return -1;
int i;
for (i = 0; i < num_entries; ++i) {
if (entries[i].vid == vid && entries[i].pid == pid)
return i;
}
return -1;
}
// Check if device needs a custom number of naxes
static int SDL_Haptic_Get_Naxes(Uint16 vid, Uint16 pid)
{
int num_entries = 0, index = 0, naxes = -1;
SDL_Haptic_VIDPID_Naxes *naxes_list = NULL;
SDL_Haptic_Load_Axes_List(&naxes_list, &num_entries);
if (!num_entries || !naxes_list)
return -1;
// Perform "wildcard" pass
index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, 0xffff, 0xffff);
if (index >= 0)
naxes = naxes_list[index].naxes;
index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, vid, pid);
if (index >= 0)
naxes = naxes_list[index].naxes;
SDL_free(naxes_list);
return naxes;
}
static SDL_Haptic *SDL_haptics = NULL;
#define CHECK_HAPTIC_MAGIC(haptic, result) \
if (!SDL_ObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC)) { \
SDL_InvalidParamError("haptic"); \
return result; \
}
bool SDL_InitHaptics(void)
{
return SDL_SYS_HapticInit();
}
static bool SDL_GetHapticIndex(SDL_HapticID instance_id, int *driver_index)
{
int num_haptics, device_index;
if (instance_id > 0) {
num_haptics = SDL_SYS_NumHaptics();
for (device_index = 0; device_index < num_haptics; ++device_index) {
SDL_HapticID haptic_id = SDL_SYS_HapticInstanceID(device_index);
if (haptic_id == instance_id) {
*driver_index = device_index;
return true;
}
}
}
SDL_SetError("Haptic device %" SDL_PRIu32 " not found", instance_id);
return false;
}
SDL_HapticID *SDL_GetHaptics(int *count)
{
int device_index;
int haptic_index = 0, num_haptics = 0;
SDL_HapticID *haptics;
num_haptics = SDL_SYS_NumHaptics();
haptics = (SDL_HapticID *)SDL_malloc((num_haptics + 1) * sizeof(*haptics));
if (haptics) {
if (count) {
*count = num_haptics;
}
for (device_index = 0; device_index < num_haptics; ++device_index) {
haptics[haptic_index] = SDL_SYS_HapticInstanceID(device_index);
SDL_assert(haptics[haptic_index] > 0);
++haptic_index;
}
haptics[haptic_index] = 0;
} else {
if (count) {
*count = 0;
}
}
return haptics;
}
const char *SDL_GetHapticNameForID(SDL_HapticID instance_id)
{
int device_index;
const char *name = NULL;
if (SDL_GetHapticIndex(instance_id, &device_index)) {
name = SDL_GetPersistentString(SDL_SYS_HapticName(device_index));
}
return name;
}
SDL_Haptic *SDL_OpenHaptic(SDL_HapticID instance_id)
{
SDL_Haptic *haptic;
SDL_Haptic *hapticlist;
const char *name;
int device_index = 0;
if (!SDL_GetHapticIndex(instance_id, &device_index)) {
return NULL;
}
hapticlist = SDL_haptics;
/* If the haptic device is already open, return it
* it is important that we have a single haptic device for each instance id
*/
while (hapticlist) {
if (instance_id == hapticlist->instance_id) {
haptic = hapticlist;
++haptic->ref_count;
return haptic;
}
hapticlist = hapticlist->next;
}
// Create the haptic device
haptic = (SDL_Haptic *)SDL_calloc(1, sizeof(*haptic));
if (!haptic) {
return NULL;
}
// Initialize the haptic device
SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
haptic->instance_id = instance_id;
haptic->rumble_id = -1;
if (!SDL_SYS_HapticOpen(haptic)) {
SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
SDL_free(haptic);
return NULL;
}
if (!haptic->name) {
name = SDL_SYS_HapticName(device_index);
if (name) {
haptic->name = SDL_strdup(name);
}
}
// Add haptic to list
++haptic->ref_count;
// Link the haptic in the list
haptic->next = SDL_haptics;
SDL_haptics = haptic;
// Disable autocenter and set gain to max.
if (haptic->supported & SDL_HAPTIC_GAIN) {
SDL_SetHapticGain(haptic, 100);
}
if (haptic->supported & SDL_HAPTIC_AUTOCENTER) {
SDL_SetHapticAutocenter(haptic, 0);
}
return haptic;
}
SDL_Haptic *SDL_GetHapticFromID(SDL_HapticID instance_id)
{
SDL_Haptic *haptic;
for (haptic = SDL_haptics; haptic; haptic = haptic->next) {
if (instance_id == haptic->instance_id) {
break;
}
}
return haptic;
}
SDL_HapticID SDL_GetHapticID(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, 0);
return haptic->instance_id;
}
const char *SDL_GetHapticName(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, NULL);
return SDL_GetPersistentString(haptic->name);
}
bool SDL_IsMouseHaptic(void)
{
if (SDL_SYS_HapticMouse() < 0) {
return false;
}
return true;
}
SDL_Haptic *SDL_OpenHapticFromMouse(void)
{
int device_index;
device_index = SDL_SYS_HapticMouse();
if (device_index < 0) {
SDL_SetError("Haptic: Mouse isn't a haptic device.");
return NULL;
}
return SDL_OpenHaptic(device_index);
}
bool SDL_IsJoystickHaptic(SDL_Joystick *joystick)
{
bool result = false;
SDL_LockJoysticks();
{
// Must be a valid joystick
if (SDL_IsJoystickValid(joystick) &&
!SDL_IsGamepad(SDL_GetJoystickID(joystick))) {
result = SDL_SYS_JoystickIsHaptic(joystick);
}
}
SDL_UnlockJoysticks();
return result;
}
SDL_Haptic *SDL_OpenHapticFromJoystick(SDL_Joystick *joystick)
{
SDL_Haptic *haptic;
SDL_Haptic *hapticlist;
SDL_LockJoysticks();
{
// Must be a valid joystick
if (!SDL_IsJoystickValid(joystick)) {
SDL_SetError("Haptic: Joystick isn't valid.");
SDL_UnlockJoysticks();
return NULL;
}
// Joystick must be haptic
if (SDL_IsGamepad(SDL_GetJoystickID(joystick)) ||
!SDL_SYS_JoystickIsHaptic(joystick)) {
SDL_SetError("Haptic: Joystick isn't a haptic device.");
SDL_UnlockJoysticks();
return NULL;
}
hapticlist = SDL_haptics;
// Check to see if joystick's haptic is already open
while (hapticlist) {
if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick)) {
haptic = hapticlist;
++haptic->ref_count;
SDL_UnlockJoysticks();
return haptic;
}
hapticlist = hapticlist->next;
}
// Create the haptic device
haptic = (SDL_Haptic *)SDL_calloc(1, sizeof(*haptic));
if (!haptic) {
SDL_UnlockJoysticks();
return NULL;
}
/* Initialize the haptic device
* This function should fill in the instance ID and name.
*/
SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
haptic->rumble_id = -1;
if (!SDL_SYS_HapticOpenFromJoystick(haptic, joystick)) {
SDL_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed.");
SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
SDL_free(haptic);
SDL_UnlockJoysticks();
return NULL;
}
SDL_assert(haptic->instance_id != 0);
}
SDL_UnlockJoysticks();
// Check if custom number of haptic axes was defined
Uint16 vid = SDL_GetJoystickVendor(joystick);
Uint16 pid = SDL_GetJoystickProduct(joystick);
int general_axes = SDL_GetNumJoystickAxes(joystick);
int naxes = SDL_Haptic_Get_Naxes(vid, pid);
if (naxes > 0)
haptic->naxes = naxes;
// Limit to the actual number of axes found on the device
if (general_axes >= 0 && naxes > general_axes)
haptic->naxes = general_axes;
// Add haptic to list
++haptic->ref_count;
// Link the haptic in the list
haptic->next = SDL_haptics;
SDL_haptics = haptic;
return haptic;
}
void SDL_CloseHaptic(SDL_Haptic *haptic)
{
int i;
SDL_Haptic *hapticlist;
SDL_Haptic *hapticlistprev;
CHECK_HAPTIC_MAGIC(haptic,);
// Check if it's still in use
if (--haptic->ref_count > 0) {
return;
}
// Close it, properly removing effects if needed
for (i = 0; i < haptic->neffects; i++) {
if (haptic->effects[i].hweffect != NULL) {
SDL_DestroyHapticEffect(haptic, i);
}
}
SDL_SYS_HapticClose(haptic);
SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
// Remove from the list
hapticlist = SDL_haptics;
hapticlistprev = NULL;
while (hapticlist) {
if (haptic == hapticlist) {
if (hapticlistprev) {
// unlink this entry
hapticlistprev->next = hapticlist->next;
} else {
SDL_haptics = haptic->next;
}
break;
}
hapticlistprev = hapticlist;
hapticlist = hapticlist->next;
}
// Free the data associated with this device
SDL_free(haptic->name);
SDL_free(haptic);
}
void SDL_QuitHaptics(void)
{
while (SDL_haptics) {
SDL_CloseHaptic(SDL_haptics);
}
SDL_SYS_HapticQuit();
}
int SDL_GetMaxHapticEffects(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, -1);
return haptic->neffects;
}
int SDL_GetMaxHapticEffectsPlaying(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, -1);
return haptic->nplaying;
}
Uint32 SDL_GetHapticFeatures(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, 0);
return haptic->supported;
}
int SDL_GetNumHapticAxes(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, -1);
return haptic->naxes;
}
bool SDL_HapticEffectSupported(SDL_Haptic *haptic, const SDL_HapticEffect *effect)
{
CHECK_HAPTIC_MAGIC(haptic, false);
if (!effect) {
return false;
}
if ((haptic->supported & effect->type) != 0) {
return true;
}
return false;
}
int SDL_CreateHapticEffect(SDL_Haptic *haptic, const SDL_HapticEffect *effect)
{
int i;
CHECK_HAPTIC_MAGIC(haptic, -1);
if (!effect) {
SDL_InvalidParamError("effect");
return -1;
}
// Check to see if effect is supported
if (SDL_HapticEffectSupported(haptic, effect) == false) {
SDL_SetError("Haptic: Effect not supported by haptic device.");
return -1;
}
// See if there's a free slot
for (i = 0; i < haptic->neffects; i++) {
if (haptic->effects[i].hweffect == NULL) {
// Now let the backend create the real effect
if (!SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect)) {
return -1; // Backend failed to create effect
}
SDL_memcpy(&haptic->effects[i].effect, effect,
sizeof(SDL_HapticEffect));
return i;
}
}
SDL_SetError("Haptic: Device has no free space left.");
return -1;
}
static bool ValidEffect(SDL_Haptic *haptic, int effect)
{
if ((effect < 0) || (effect >= haptic->neffects)) {
SDL_SetError("Haptic: Invalid effect identifier.");
return false;
}
return true;
}
bool SDL_UpdateHapticEffect(SDL_Haptic *haptic, int effect, const SDL_HapticEffect *data)
{
CHECK_HAPTIC_MAGIC(haptic, false);
if (!ValidEffect(haptic, effect)) {
return false;
}
if (!data) {
return SDL_InvalidParamError("data");
}
// Can't change type dynamically.
if (data->type != haptic->effects[effect].effect.type) {
return SDL_SetError("Haptic: Updating effect type is illegal.");
}
// Updates the effect
if (!SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data)) {
return false;
}
SDL_memcpy(&haptic->effects[effect].effect, data,
sizeof(SDL_HapticEffect));
return true;
}
bool SDL_RunHapticEffect(SDL_Haptic *haptic, int effect, Uint32 iterations)
{
CHECK_HAPTIC_MAGIC(haptic, false);
if (!ValidEffect(haptic, effect)) {
return false;
}
// Run the effect
if (!SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations)) {
return false;
}
return true;
}
bool SDL_StopHapticEffect(SDL_Haptic *haptic, int effect)
{
CHECK_HAPTIC_MAGIC(haptic, false);
if (!ValidEffect(haptic, effect)) {
return false;
}
// Stop the effect
if (!SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect])) {
return false;
}
return true;
}
void SDL_DestroyHapticEffect(SDL_Haptic *haptic, int effect)
{
CHECK_HAPTIC_MAGIC(haptic,);
if (!ValidEffect(haptic, effect)) {
return;
}
// Not allocated
if (haptic->effects[effect].hweffect == NULL) {
return;
}
SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]);
}
bool SDL_GetHapticEffectStatus(SDL_Haptic *haptic, int effect)
{
CHECK_HAPTIC_MAGIC(haptic, false);
if (!ValidEffect(haptic, effect)) {
return false;
}
if (!(haptic->supported & SDL_HAPTIC_STATUS)) {
return SDL_SetError("Haptic: Device does not support status queries.");
}
SDL_ClearError();
return (SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]) > 0);
}
bool SDL_SetHapticGain(SDL_Haptic *haptic, int gain)
{
const char *env;
int real_gain, max_gain;
CHECK_HAPTIC_MAGIC(haptic, false);
if (!(haptic->supported & SDL_HAPTIC_GAIN)) {
return SDL_SetError("Haptic: Device does not support setting gain.");
}
if ((gain < 0) || (gain > 100)) {
return SDL_SetError("Haptic: Gain must be between 0 and 100.");
}
// The user can use an environment variable to override the max gain.
env = SDL_getenv("SDL_HAPTIC_GAIN_MAX");
if (env) {
max_gain = SDL_atoi(env);
// Check for sanity.
if (max_gain < 0) {
max_gain = 0;
} else if (max_gain > 100) {
max_gain = 100;
}
// We'll scale it linearly with SDL_HAPTIC_GAIN_MAX
real_gain = (gain * max_gain) / 100;
} else {
real_gain = gain;
}
return SDL_SYS_HapticSetGain(haptic, real_gain);
}
bool SDL_SetHapticAutocenter(SDL_Haptic *haptic, int autocenter)
{
CHECK_HAPTIC_MAGIC(haptic, false);
if (!(haptic->supported & SDL_HAPTIC_AUTOCENTER)) {
return SDL_SetError("Haptic: Device does not support setting autocenter.");
}
if ((autocenter < 0) || (autocenter > 100)) {
return SDL_SetError("Haptic: Autocenter must be between 0 and 100.");
}
return SDL_SYS_HapticSetAutocenter(haptic, autocenter);
}
bool SDL_PauseHaptic(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, false);
if (!(haptic->supported & SDL_HAPTIC_PAUSE)) {
return SDL_SetError("Haptic: Device does not support setting pausing.");
}
return SDL_SYS_HapticPause(haptic);
}
bool SDL_ResumeHaptic(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, false);
if (!(haptic->supported & SDL_HAPTIC_PAUSE)) {
return true; // Not going to be paused, so we pretend it's unpaused.
}
return SDL_SYS_HapticResume(haptic);
}
bool SDL_StopHapticEffects(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, false);
return SDL_SYS_HapticStopAll(haptic);
}
bool SDL_HapticRumbleSupported(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, false);
// Most things can use SINE, but XInput only has LEFTRIGHT.
return (haptic->supported & (SDL_HAPTIC_SINE | SDL_HAPTIC_LEFTRIGHT)) != 0;
}
bool SDL_InitHapticRumble(SDL_Haptic *haptic)
{
SDL_HapticEffect *efx = &haptic->rumble_effect;
CHECK_HAPTIC_MAGIC(haptic, false);
// Already allocated.
if (haptic->rumble_id >= 0) {
return true;
}
SDL_zerop(efx);
if (haptic->supported & SDL_HAPTIC_SINE) {
efx->type = SDL_HAPTIC_SINE;
efx->periodic.direction.type = SDL_HAPTIC_CARTESIAN;
efx->periodic.period = 1000;
efx->periodic.magnitude = 0x4000;
efx->periodic.length = 5000;
efx->periodic.attack_length = 0;
efx->periodic.fade_length = 0;
} else if (haptic->supported & SDL_HAPTIC_LEFTRIGHT) { // XInput?
efx->type = SDL_HAPTIC_LEFTRIGHT;
efx->leftright.length = 5000;
efx->leftright.large_magnitude = 0x4000;
efx->leftright.small_magnitude = 0x4000;
} else {
return SDL_SetError("Device doesn't support rumble");
}
haptic->rumble_id = SDL_CreateHapticEffect(haptic, &haptic->rumble_effect);
if (haptic->rumble_id >= 0) {
return true;
}
return false;
}
bool SDL_PlayHapticRumble(SDL_Haptic *haptic, float strength, Uint32 length)
{
SDL_HapticEffect *efx;
Sint16 magnitude;
CHECK_HAPTIC_MAGIC(haptic, false);
if (haptic->rumble_id < 0) {
return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
}
// Clamp strength.
if (strength > 1.0f) {
strength = 1.0f;
} else if (strength < 0.0f) {
strength = 0.0f;
}
magnitude = (Sint16)(32767.0f * strength);
efx = &haptic->rumble_effect;
if (efx->type == SDL_HAPTIC_SINE) {
efx->periodic.magnitude = magnitude;
efx->periodic.length = length;
} else if (efx->type == SDL_HAPTIC_LEFTRIGHT) {
efx->leftright.small_magnitude = efx->leftright.large_magnitude = magnitude;
efx->leftright.length = length;
} else {
SDL_assert(!"This should have been caught elsewhere");
}
if (!SDL_UpdateHapticEffect(haptic, haptic->rumble_id, &haptic->rumble_effect)) {
return false;
}
return SDL_RunHapticEffect(haptic, haptic->rumble_id, 1);
}
bool SDL_StopHapticRumble(SDL_Haptic *haptic)
{
CHECK_HAPTIC_MAGIC(haptic, false);
if (haptic->rumble_id < 0) {
return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
}
return SDL_StopHapticEffect(haptic, haptic->rumble_id);
}

28
thirdparty/sdl/haptic/SDL_haptic_c.h vendored Normal file
View File

@@ -0,0 +1,28 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_haptic_c_h_
#define SDL_haptic_c_h_
extern bool SDL_InitHaptics(void);
extern void SDL_QuitHaptics(void);
#endif // SDL_haptic_c_h_

194
thirdparty/sdl/haptic/SDL_syshaptic.h vendored Normal file
View File

@@ -0,0 +1,194 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_syshaptic_h_
#define SDL_syshaptic_h_
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
struct haptic_effect
{
SDL_HapticEffect effect; // The current event
struct haptic_hweffect *hweffect; // The hardware behind the event
};
/*
* The real SDL_Haptic struct.
*/
struct SDL_Haptic
{
SDL_HapticID instance_id; // Device instance, monotonically increasing from 0
char *name; // Device name - system dependent
struct haptic_effect *effects; // Allocated effects
int neffects; // Maximum amount of effects
int nplaying; // Maximum amount of effects to play at the same time
Uint32 supported; // Supported effects and features
int naxes; // Number of axes on the device.
struct haptic_hwdata *hwdata; // Driver dependent
int ref_count; // Count for multiple opens
int rumble_id; // ID of rumble effect for simple rumble API.
SDL_HapticEffect rumble_effect; // Rumble effect.
struct SDL_Haptic *next; // pointer to next haptic we have allocated
};
/*
* Scans the system for haptic devices.
*
* Returns number of devices on success, -1 on error.
*/
extern bool SDL_SYS_HapticInit(void);
// Function to return the number of haptic devices plugged in right now
extern int SDL_SYS_NumHaptics(void);
/*
* Gets the instance ID of the haptic device
*/
extern SDL_HapticID SDL_SYS_HapticInstanceID(int index);
/*
* Gets the device dependent name of the haptic device
*/
extern const char *SDL_SYS_HapticName(int index);
/*
* Opens the haptic device for usage. The haptic device should have
* the index value set previously.
*/
extern bool SDL_SYS_HapticOpen(SDL_Haptic *haptic);
/*
* Returns the index of the haptic core pointer or -1 if none is found.
*/
extern int SDL_SYS_HapticMouse(void);
/*
* Checks to see if the joystick has haptic capabilities.
*/
extern bool SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick);
/*
* Opens the haptic device for usage using the same device as
* the joystick.
*/
extern bool SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic,
SDL_Joystick *joystick);
/*
* Checks to see if haptic device and joystick device are the same.
*
* Returns true if they are the same, false if they aren't.
*/
extern bool SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic,
SDL_Joystick *joystick);
/*
* Closes a haptic device after usage.
*/
extern void SDL_SYS_HapticClose(SDL_Haptic *haptic);
/*
* Performs a cleanup on the haptic subsystem.
*/
extern void SDL_SYS_HapticQuit(void);
/*
* Creates a new haptic effect on the haptic device using base
* as a template for the effect.
*/
extern bool SDL_SYS_HapticNewEffect(SDL_Haptic *haptic,
struct haptic_effect *effect,
const SDL_HapticEffect *base);
/*
* Updates the haptic effect on the haptic device using data
* as a template.
*/
extern bool SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic,
struct haptic_effect *effect,
const SDL_HapticEffect *data);
/*
* Runs the effect on the haptic device.
*/
extern bool SDL_SYS_HapticRunEffect(SDL_Haptic *haptic,
struct haptic_effect *effect,
Uint32 iterations);
/*
* Stops the effect on the haptic device.
*/
extern bool SDL_SYS_HapticStopEffect(SDL_Haptic *haptic,
struct haptic_effect *effect);
/*
* Cleanups up the effect on the haptic device.
*/
extern void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic,
struct haptic_effect *effect);
/*
* Queries the device for the status of effect.
*
* Returns 0 if device is stopped, >0 if device is playing and
* -1 on error.
*/
extern int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic,
struct haptic_effect *effect);
/*
* Sets the global gain of the haptic device.
*/
extern bool SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain);
/*
* Sets the autocenter feature of the haptic device.
*/
extern bool SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter);
/*
* Pauses the haptic device.
*/
extern bool SDL_SYS_HapticPause(SDL_Haptic *haptic);
/*
* Unpauses the haptic device.
*/
extern bool SDL_SYS_HapticResume(SDL_Haptic *haptic);
/*
* Stops all the currently playing haptic effects on the device.
*/
extern bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic);
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif
#endif // SDL_syshaptic_h_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
// Things named "Master" were renamed to "Main" in macOS 12.0's SDK.
#include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MIN_REQUIRED < 120000
#define kIOMainPortDefault kIOMasterPortDefault
#endif
extern bool MacHaptic_MaybeAddDevice(io_object_t device);
extern bool MacHaptic_MaybeRemoveDevice(io_object_t device);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#include "SDL_windowshaptic_c.h"
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
extern bool SDL_DINPUT_HapticInit(void);
extern bool SDL_DINPUT_HapticMaybeAddDevice(const DIDEVICEINSTANCE *pdidInstance);
extern bool SDL_DINPUT_HapticMaybeRemoveDevice(const DIDEVICEINSTANCE *pdidInstance);
extern bool SDL_DINPUT_HapticOpen(SDL_Haptic *haptic, SDL_hapticlist_item *item);
extern bool SDL_DINPUT_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick);
extern bool SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick);
extern void SDL_DINPUT_HapticClose(SDL_Haptic *haptic);
extern void SDL_DINPUT_HapticQuit(void);
extern bool SDL_DINPUT_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect, const SDL_HapticEffect *base);
extern bool SDL_DINPUT_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, const SDL_HapticEffect *data);
extern bool SDL_DINPUT_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations);
extern bool SDL_DINPUT_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect);
extern void SDL_DINPUT_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect);
extern int SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect);
extern bool SDL_DINPUT_HapticSetGain(SDL_Haptic *haptic, int gain);
extern bool SDL_DINPUT_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter);
extern bool SDL_DINPUT_HapticPause(SDL_Haptic *haptic);
extern bool SDL_DINPUT_HapticResume(SDL_Haptic *haptic);
extern bool SDL_DINPUT_HapticStopAll(SDL_Haptic *haptic);
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,369 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_HAPTIC_DINPUT
#include "../SDL_syshaptic.h"
#include "../../joystick/SDL_sysjoystick.h" // For the real SDL_Joystick
#include "../../joystick/windows/SDL_windowsjoystick_c.h" // For joystick hwdata
#include "../../joystick/windows/SDL_xinputjoystick_c.h" // For xinput rumble
#include "SDL_windowshaptic_c.h"
#include "SDL_dinputhaptic_c.h"
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
/*
* Internal stuff.
*/
SDL_hapticlist_item *SDL_hapticlist = NULL;
static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
static int numhaptics = 0;
/*
* Initializes the haptic subsystem.
*/
bool SDL_SYS_HapticInit(void)
{
JoyStick_DeviceData *device;
if (!SDL_DINPUT_HapticInit()) {
return false;
}
/* The joystick subsystem will usually be initialized before haptics,
* so the initial HapticMaybeAddDevice() calls from the joystick
* subsystem will arrive too early to create haptic devices. We will
* invoke those callbacks again here to pick up any joysticks that
* were added prior to haptics initialization. */
for (device = SYS_Joystick; device; device = device->pNext) {
SDL_DINPUT_HapticMaybeAddDevice(&device->dxdevice);
}
return true;
}
bool SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item)
{
if (!SDL_hapticlist_tail) {
SDL_hapticlist = SDL_hapticlist_tail = item;
} else {
SDL_hapticlist_tail->next = item;
SDL_hapticlist_tail = item;
}
// Device has been added.
++numhaptics;
return true;
}
bool SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item)
{
const bool result = item->haptic ? true : false;
if (prev) {
prev->next = item->next;
} else {
SDL_assert(SDL_hapticlist == item);
SDL_hapticlist = item->next;
}
if (item == SDL_hapticlist_tail) {
SDL_hapticlist_tail = prev;
}
--numhaptics;
// !!! TODO: Send a haptic remove event?
SDL_free(item);
return result;
}
int SDL_SYS_NumHaptics(void)
{
return numhaptics;
}
static SDL_hapticlist_item *HapticByDevIndex(int device_index)
{
SDL_hapticlist_item *item = SDL_hapticlist;
if ((device_index < 0) || (device_index >= numhaptics)) {
return NULL;
}
while (device_index > 0) {
SDL_assert(item != NULL);
--device_index;
item = item->next;
}
return item;
}
static SDL_hapticlist_item *HapticByInstanceID(SDL_HapticID instance_id)
{
SDL_hapticlist_item *item;
for (item = SDL_hapticlist; item; item = item->next) {
if (instance_id == item->instance_id) {
return item;
}
}
return NULL;
}
SDL_HapticID SDL_SYS_HapticInstanceID(int index)
{
SDL_hapticlist_item *item = HapticByDevIndex(index);
if (item) {
return item->instance_id;
}
return 0;
}
/*
* Return the name of a haptic device, does not need to be opened.
*/
const char *SDL_SYS_HapticName(int index)
{
SDL_hapticlist_item *item = HapticByDevIndex(index);
return item->name;
}
/*
* Opens a haptic device for usage.
*/
bool SDL_SYS_HapticOpen(SDL_Haptic *haptic)
{
SDL_hapticlist_item *item = HapticByInstanceID(haptic->instance_id);
return SDL_DINPUT_HapticOpen(haptic, item);
}
/*
* Opens a haptic device from first mouse it finds for usage.
*/
int SDL_SYS_HapticMouse(void)
{
#ifdef SDL_HAPTIC_DINPUT
SDL_hapticlist_item *item;
int index = 0;
// Grab the first mouse haptic device we find.
for (item = SDL_hapticlist; item; item = item->next) {
if (item->capabilities.dwDevType == DI8DEVCLASS_POINTER) {
return index;
}
++index;
}
#endif // SDL_HAPTIC_DINPUT
return -1;
}
/*
* Checks to see if a joystick has haptic features.
*/
bool SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
{
if (joystick->driver != &SDL_WINDOWS_JoystickDriver) {
return false;
}
if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
return true;
}
return false;
}
/*
* Checks to see if the haptic device and joystick are in reality the same.
*/
bool SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
{
if (joystick->driver != &SDL_WINDOWS_JoystickDriver) {
return false;
}
return SDL_DINPUT_JoystickSameHaptic(haptic, joystick);
}
/*
* Opens a SDL_Haptic from a SDL_Joystick.
*/
bool SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
{
SDL_assert(joystick->driver == &SDL_WINDOWS_JoystickDriver);
return SDL_DINPUT_HapticOpenFromJoystick(haptic, joystick);
}
/*
* Closes the haptic device.
*/
void SDL_SYS_HapticClose(SDL_Haptic *haptic)
{
if (haptic->hwdata) {
// Free effects.
SDL_free(haptic->effects);
haptic->effects = NULL;
haptic->neffects = 0;
// Clean up
SDL_DINPUT_HapticClose(haptic);
// Free
SDL_free(haptic->hwdata);
haptic->hwdata = NULL;
}
}
/*
* Clean up after system specific haptic stuff
*/
void SDL_SYS_HapticQuit(void)
{
SDL_hapticlist_item *item;
SDL_hapticlist_item *next = NULL;
for (item = SDL_hapticlist; item; item = next) {
/* Opened and not closed haptics are leaked, this is on purpose.
* Close your haptic devices after usage. */
// !!! FIXME: (...is leaking on purpose a good idea?) - No, of course not.
next = item->next;
SDL_free(item->name);
SDL_free(item);
}
SDL_DINPUT_HapticQuit();
numhaptics = 0;
SDL_hapticlist = NULL;
SDL_hapticlist_tail = NULL;
}
/*
* Creates a new haptic effect.
*/
bool SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
const SDL_HapticEffect *base)
{
bool result;
// Alloc the effect.
effect->hweffect = (struct haptic_hweffect *) SDL_calloc(1, sizeof(struct haptic_hweffect));
if (!effect->hweffect) {
return false;
}
result = SDL_DINPUT_HapticNewEffect(haptic, effect, base);
if (!result) {
SDL_free(effect->hweffect);
effect->hweffect = NULL;
}
return result;
}
/*
* Updates an effect.
*/
bool SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, const SDL_HapticEffect *data)
{
return SDL_DINPUT_HapticUpdateEffect(haptic, effect, data);
}
/*
* Runs an effect.
*/
bool SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations)
{
return SDL_DINPUT_HapticRunEffect(haptic, effect, iterations);
}
/*
* Stops an effect.
*/
bool SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
{
return SDL_DINPUT_HapticStopEffect(haptic, effect);
}
/*
* Frees the effect.
*/
void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
{
SDL_DINPUT_HapticDestroyEffect(haptic, effect);
SDL_free(effect->hweffect);
effect->hweffect = NULL;
}
/*
* Gets the status of a haptic effect.
*/
int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect)
{
return SDL_DINPUT_HapticGetEffectStatus(haptic, effect);
}
/*
* Sets the gain.
*/
bool SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
{
return SDL_DINPUT_HapticSetGain(haptic, gain);
}
/*
* Sets the autocentering.
*/
bool SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
{
return SDL_DINPUT_HapticSetAutocenter(haptic, autocenter);
}
/*
* Pauses the device.
*/
bool SDL_SYS_HapticPause(SDL_Haptic *haptic)
{
return SDL_DINPUT_HapticPause(haptic);
}
/*
* Pauses the device.
*/
bool SDL_SYS_HapticResume(SDL_Haptic *haptic)
{
return SDL_DINPUT_HapticResume(haptic);
}
/*
* Stops all the playing effects on the device.
*/
bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
{
return SDL_DINPUT_HapticStopAll(haptic);
}
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif
#endif // SDL_HAPTIC_DINPUT

View File

@@ -0,0 +1,87 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_windowshaptic_c_h_
#define SDL_windowshaptic_c_h_
#include "../SDL_syshaptic.h"
#include "../../core/windows/SDL_directx.h"
#include "../../core/windows/SDL_xinput.h"
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
extern "C" {
#endif
/*
* Haptic system hardware data.
*/
struct haptic_hwdata
{
#ifdef SDL_HAPTIC_DINPUT
LPDIRECTINPUTDEVICE8 device;
#endif
DWORD axes[3]; // Axes to use.
bool is_joystick; // Device is loaded as joystick.
SDL_Thread *thread;
SDL_Mutex *mutex;
Uint64 stopTicks;
SDL_AtomicInt stopThread;
};
/*
* Haptic system effect data.
*/
#ifdef SDL_HAPTIC_DINPUT
struct haptic_hweffect
{
DIEFFECT effect;
LPDIRECTINPUTEFFECT ref;
};
#endif
/*
* List of available haptic devices.
*/
typedef struct SDL_hapticlist_item
{
SDL_HapticID instance_id;
char *name;
SDL_Haptic *haptic;
#ifdef SDL_HAPTIC_DINPUT
DIDEVICEINSTANCE instance;
DIDEVCAPS capabilities;
#endif
struct SDL_hapticlist_item *next;
} SDL_hapticlist_item;
extern SDL_hapticlist_item *SDL_hapticlist;
extern bool SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item);
extern bool SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item);
// Ends C function definitions when using C++
#ifdef __cplusplus
}
#endif
#endif // SDL_windowshaptic_c_h_