21#include "../../SDL_internal.h"
23#ifdef SDL_HAPTIC_LINUX
27#include "../SDL_syshaptic.h"
29#include "../../joystick/SDL_sysjoystick.h"
30#include "../../joystick/linux/SDL_sysjoystick_c.h"
31#include "../../core/linux/SDL_udev.h"
34#include <linux/input.h>
43# define M_PI 3.14159265358979323846
49static int MaybeAddDevice(
const char *
path);
51static int MaybeRemoveDevice(
const char *
path);
52static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type,
int udev_class,
const char *devpath);
82 struct ff_effect effect;
87static int numhaptics = 0;
89#define test_bit(nr, addr) \
90 (((1UL << ((nr) & 31)) & (((const unsigned int *) addr)[(nr) >> 5])) != 0)
91#define EV_TEST(ev,f) \
92 if (test_bit((ev), features)) ret |= (f);
101 unsigned long features[1 + FF_MAX /
sizeof(
unsigned long)];
105 if (ioctl(
fd, EVIOCGBIT(EV_FF,
sizeof(features)), features) < 0) {
106 return SDL_SetError(
"Haptic: Unable to get device's features: %s",
139 unsigned long argp[40];
142 if (ioctl(
fd, EVIOCGBIT(EV_KEY,
sizeof(argp)), argp) < 0) {
147 if (test_bit(BTN_MOUSE, argp) != 0) {
160 const char joydev_pattern[] =
"/dev/input/event%d";
169 for (
j = 0;
j < MAX_HAPTICS; ++
j) {
171 snprintf(
path, PATH_MAX, joydev_pattern,
i++);
172 MaybeAddDevice(
path);
176 if (SDL_UDEV_Init() < 0) {
180 if ( SDL_UDEV_AddCallback(haptic_udev_callback) < 0) {
182 return SDL_SetError(
"Could not setup haptic <-> udev callback");
199HapticByDevIndex(
int device_index)
203 if ((device_index < 0) || (device_index >= numhaptics)) {
207 while (device_index > 0) {
217static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type,
int udev_class,
const char *devpath)
219 if (devpath ==
NULL || !(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
225 case SDL_UDEV_DEVICEADDED:
226 MaybeAddDevice(devpath);
229 case SDL_UDEV_DEVICEREMOVED:
230 MaybeRemoveDevice(devpath);
241MaybeAddDevice(
const char *
path)
253 if (stat(
path, &sb) != 0) {
259 if (item->dev_num == sb.st_rdev) {
265 fd = open(
path, O_RDWR, 0);
270#ifdef DEBUG_INPUT_EVENTS
271 printf(
"Checking %s\n",
path);
275 success = EV_IsHaptic(
fd);
287 if (item->fname ==
NULL) {
292 item->dev_num = sb.st_rdev;
295 if (SDL_hapticlist_tail ==
NULL) {
298 SDL_hapticlist_tail->
next = item;
299 SDL_hapticlist_tail = item;
311MaybeRemoveDevice(
const char*
path)
331 if (item == SDL_hapticlist_tail) {
332 SDL_hapticlist_tail = prev;
354SDL_SYS_HapticNameFromFD(
int fd)
356 static char namebuf[128];
359 if (ioctl(
fd, EVIOCGNAME(
sizeof(namebuf)), namebuf) <= 0) {
377 item = HapticByDevIndex(
index);
380 fd = open(item->fname, O_RDONLY, 0);
384 name = SDL_SYS_HapticNameFromFD(
fd);
400SDL_SYS_HapticOpenFromFD(SDL_Haptic *
haptic,
int fd)
413 haptic->supported = EV_IsHaptic(
fd);
417 if (ioctl(
fd, EVIOCGEFFECTS, &
haptic->neffects) < 0) {
418 SDL_SetError(
"Haptic: Unable to query device memory: %s",
456 item = HapticByDevIndex(
haptic->index);
458 fd = open(item->fname, O_RDWR, 0);
461 item->fname, strerror(errno));
465 ret = SDL_SYS_HapticOpenFromFD(
haptic,
fd);
483 int device_index = 0;
488 fd = open(item->fname, O_RDWR, 0);
491 item->fname, strerror(errno));
495 if (EV_IsMouse(
fd)) {
515 return EV_IsHaptic(joystick->hwdata->fd);
540 int device_index = 0;
547 if (
SDL_strcmp(item->fname, joystick->hwdata->fname) == 0) {
552 haptic->index = device_index;
554 if (device_index >= MAX_HAPTICS) {
555 return SDL_SetError(
"Haptic: Joystick doesn't have Haptic capabilities");
558 fd = open(joystick->hwdata->fname, O_RDWR, 0);
561 joystick->hwdata->fname, strerror(errno));
563 ret = SDL_SYS_HapticOpenFromFD(
haptic,
fd);
588 close(
haptic->hwdata->fd);
619 SDL_UDEV_DelCallback(haptic_udev_callback);
625 SDL_hapticlist_tail =
NULL;
644 ff_button = BTN_GAMEPAD +
button - 1;
672 tmp = ((
src->dir[0] % 36000) * 0x8000) / 18000;
685 tmp = ((
src->dir[0]) + 9000) % 36000;
686 tmp = (tmp * 0x8000) / 18000;
692 *dest = (
src->dir[0] >= 0 ? 0x4000 : 0xC000);
693 else if (!
src->dir[0])
694 *dest = (
src->dir[1] >= 0 ? 0x8000 : 0);
707 tmp = (((
Sint32) (
f * 18000. / M_PI)) + 45000) % 36000;
708 tmp = (tmp * 0x8000) / 18000;
714 return SDL_SetError(
"Haptic: Unsupported direction type.");
721#define CLAMP(x) (((x) > 32767) ? 32767 : x)
736 SDL_memset(dest, 0,
sizeof(
struct ff_effect));
740 constant = &
src->constant;
743 dest->
type = FF_CONSTANT;
744 if (SDL_SYS_ToDirection(&dest->direction, &constant->
direction) == -1)
749 0 : CLAMP(constant->
length);
750 dest->replay.delay = CLAMP(constant->
delay);
753 dest->trigger.button = SDL_SYS_ToButton(constant->
button);
754 dest->trigger.interval = CLAMP(constant->
interval);
757 dest->u.constant.level = constant->
level;
760 dest->u.constant.envelope.attack_length =
762 dest->u.constant.envelope.attack_level =
764 dest->u.constant.envelope.fade_length = CLAMP(constant->
fade_length);
765 dest->u.constant.envelope.fade_level = CLAMP(constant->
fade_level);
775 periodic = &
src->periodic;
778 dest->
type = FF_PERIODIC;
779 if (SDL_SYS_ToDirection(&dest->direction, &periodic->
direction) == -1)
784 0 : CLAMP(periodic->
length);
785 dest->replay.delay = CLAMP(periodic->
delay);
788 dest->trigger.button = SDL_SYS_ToButton(periodic->
button);
789 dest->trigger.interval = CLAMP(periodic->
interval);
793 dest->u.periodic.waveform = FF_SINE;
798 dest->u.periodic.waveform = FF_TRIANGLE;
800 dest->u.periodic.waveform = FF_SAW_UP;
802 dest->u.periodic.waveform = FF_SAW_DOWN;
803 dest->u.periodic.period = CLAMP(periodic->
period);
804 dest->u.periodic.magnitude = periodic->
magnitude;
805 dest->u.periodic.offset = periodic->
offset;
807 dest->u.periodic.phase = ((
Uint32)periodic->
phase * 0x10000U) / 36000;
810 dest->u.periodic.envelope.attack_length =
812 dest->u.periodic.envelope.attack_level =
814 dest->u.periodic.envelope.fade_length = CLAMP(periodic->
fade_length);
815 dest->u.periodic.envelope.fade_level = CLAMP(periodic->
fade_level);
827 dest->type = FF_SPRING;
829 dest->type = FF_DAMPER;
831 dest->type = FF_INERTIA;
833 dest->type = FF_FRICTION;
839 dest->replay.delay = CLAMP(
condition->delay);
842 dest->trigger.button = SDL_SYS_ToButton(
condition->button);
843 dest->trigger.interval = CLAMP(
condition->interval);
847 dest->u.condition[0].right_saturation =
condition->right_sat[0];
848 dest->u.condition[0].left_saturation =
condition->left_sat[0];
849 dest->u.condition[0].right_coeff =
condition->right_coeff[0];
850 dest->u.condition[0].left_coeff =
condition->left_coeff[0];
851 dest->u.condition[0].deadband =
condition->deadband[0];
852 dest->u.condition[0].center =
condition->center[0];
854 dest->u.condition[1].right_saturation =
condition->right_sat[1];
855 dest->u.condition[1].left_saturation =
condition->left_sat[1];
856 dest->u.condition[1].right_coeff =
condition->right_coeff[1];
857 dest->u.condition[1].left_coeff =
condition->left_coeff[1];
858 dest->u.condition[1].deadband =
condition->deadband[1];
859 dest->u.condition[1].center =
condition->center[1];
871 dest->
type = FF_RAMP;
872 if (SDL_SYS_ToDirection(&dest->direction, &ramp->
direction) == -1)
878 dest->replay.delay = CLAMP(ramp->
delay);
881 dest->trigger.button = SDL_SYS_ToButton(ramp->
button);
882 dest->trigger.interval = CLAMP(ramp->
interval);
885 dest->u.ramp.start_level = ramp->
start;
886 dest->u.ramp.end_level = ramp->
end;
889 dest->u.ramp.envelope.attack_length = CLAMP(ramp->
attack_length);
890 dest->u.ramp.envelope.attack_level = CLAMP(ramp->
attack_level);
891 dest->u.ramp.envelope.fade_length = CLAMP(ramp->
fade_length);
892 dest->u.ramp.envelope.fade_level = CLAMP(ramp->
fade_level);
897 leftright = &
src->leftright;
900 dest->
type = FF_RUMBLE;
905 0 : CLAMP(leftright->
length);
908 dest->trigger.button = 0;
909 dest->trigger.interval = 0;
912 dest->u.rumble.strong_magnitude = CLAMP(leftright->
large_magnitude) * 2;
933 struct ff_effect *linux_effect;
943 linux_effect = &effect->
hweffect->effect;
944 if (SDL_SYS_ToFFEffect(linux_effect, base) != 0) {
947 linux_effect->id = -1;
950 if (ioctl(
haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
951 SDL_SetError(
"Haptic: Error uploading effect to the device: %s",
976 struct ff_effect linux_effect;
979 if (SDL_SYS_ToFFEffect(&linux_effect,
data) != 0) {
982 linux_effect.id = effect->
hweffect->effect.id;
985 if (ioctl(
haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
986 return SDL_SetError(
"Haptic: Error updating the effect: %s",
992 sizeof(
struct ff_effect));
1005 struct input_event run;
1009 run.code = effect->
hweffect->effect.id;
1013 if (write(
haptic->hwdata->fd, (
const void *) &run,
sizeof(run)) < 0) {
1014 return SDL_SetError(
"Haptic: Unable to run the effect: %s", strerror(errno));
1027 struct input_event stop;
1030 stop.code = effect->
hweffect->effect.id;
1033 if (write(
haptic->hwdata->fd, (
const void *) &stop,
sizeof(stop)) < 0) {
1034 return SDL_SetError(
"Haptic: Unable to stop the effect: %s",
1048 if (ioctl(
haptic->hwdata->fd, EVIOCRMFF, effect->
hweffect->effect.id) < 0) {
1049 SDL_SetError(
"Haptic: Error removing the effect from the device: %s",
1065 struct input_event ie;
1068 ie.type = EV_FF_STATUS;
1069 ie.code = effect->
hweffect->effect.id;
1071 if (write(
haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1072 return SDL_SetError(
"Haptic: Error getting device status.");
1088 struct input_event ie;
1092 ie.value = (0xFFFFUL * gain) / 100;
1094 if (write(
haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1095 return SDL_SetError(
"Haptic: Error setting gain: %s", strerror(errno));
1108 struct input_event ie;
1111 ie.code = FF_AUTOCENTER;
1112 ie.value = (0xFFFFUL * autocenter) / 100;
1114 if (write(
haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1115 return SDL_SetError(
"Haptic: Error setting autocenter: %s", strerror(errno));
1151 for (
i = 0;
i <
haptic->neffects;
i++) {
1156 (
"Haptic: Error while trying to stop all playing effects.");
#define SDL_assert(condition)
#define SDL_OutOfMemory()
The SDL haptic subsystem allows you to control haptic (force feedback) devices.
#define SDL_HAPTIC_INERTIA
Inertia effect supported - uses axes acceleration.
#define SDL_HAPTIC_SPHERICAL
Uses spherical coordinates for the direction.
#define SDL_HAPTIC_AUTOCENTER
Device can set autocenter.
#define SDL_HAPTIC_GAIN
Device can set global gain.
#define SDL_HAPTIC_SPRING
Spring effect supported - uses axes position.
#define SDL_HAPTIC_INFINITY
Used to play a device an infinite number of times.
#define SDL_HAPTIC_DAMPER
Damper effect supported - uses axes velocity.
#define SDL_HAPTIC_CUSTOM
Custom effect is supported.
#define SDL_HAPTIC_CONSTANT
Constant effect supported.
#define SDL_HAPTIC_FRICTION
Friction effect supported - uses axes movement.
#define SDL_HAPTIC_SINE
Sine wave effect supported.
#define SDL_HAPTIC_SAWTOOTHUP
Sawtoothup wave effect supported.
#define SDL_HAPTIC_POLAR
Uses polar coordinates for the direction.
#define SDL_HAPTIC_LEFTRIGHT
Left/Right effect supported.
#define SDL_HAPTIC_TRIANGLE
Triangle wave effect supported.
#define SDL_HAPTIC_RAMP
Ramp effect supported.
#define SDL_HAPTIC_CARTESIAN
Uses cartesian coordinates for the direction.
#define SDL_HAPTIC_SAWTOOTHDOWN
Sawtoothdown wave effect supported.
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
GLuint const GLchar * name
GLsizei const GLchar *const * path
void SDL_SYS_HapticClose(SDL_Haptic *haptic)
int SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
int SDL_SYS_HapticPause(SDL_Haptic *haptic)
int SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
void SDL_SYS_HapticQuit(void)
int SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
int SDL_SYS_HapticMouse(void)
int SDL_SYS_HapticUnpause(SDL_Haptic *haptic)
int SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
int SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *data)
int SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
int SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *base)
int SDL_SYS_NumHaptics(void)
const char * SDL_SYS_HapticName(int index)
void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
int SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations)
int SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
int SDL_SYS_HapticOpen(SDL_Haptic *haptic)
int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect)
int SDL_SYS_HapticInit(void)
int SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
SDL_hapticlist_item * SDL_hapticlist
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int int in j)
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
A structure containing a template for a Condition effect.
A structure containing a template for a Constant effect.
SDL_HapticDirection direction
Structure that represents a haptic direction.
A structure containing a template for a Left/Right effect.
A structure containing a template for a Periodic effect.
SDL_HapticDirection direction
A structure containing a template for a Ramp effect.
SDL_HapticDirection direction
struct SDL_hapticlist_item * next
struct haptic_hweffect * hweffect
static SDL_Haptic * haptic
The generic template for any haptic effect.