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 47 #define MAX_HAPTICS 32 49 static int MaybeAddDevice(
const char *
path);
51 static int MaybeRemoveDevice(
const char *
path);
52 static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type,
int udev_class,
const char *devpath);
82 struct ff_effect effect;
87 static 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");
199 HapticByDevIndex(
int device_index)
203 if ((device_index < 0) || (device_index >= numhaptics)) {
207 while (device_index > 0) {
217 static 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);
241 MaybeAddDevice(
const char *path)
253 if (stat(path, &sb) != 0) {
258 for (item = SDL_hapticlist; item !=
NULL; item = item->
next) {
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) {
296 SDL_hapticlist = SDL_hapticlist_tail = item;
298 SDL_hapticlist_tail->
next = item;
299 SDL_hapticlist_tail = item;
311 MaybeRemoveDevice(
const char* path)
320 for (item = SDL_hapticlist; item !=
NULL; item = item->
next) {
329 SDL_hapticlist = item->
next;
331 if (item == SDL_hapticlist_tail) {
332 SDL_hapticlist_tail = prev;
354 SDL_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);
400 SDL_SYS_HapticOpenFromFD(SDL_Haptic *
haptic,
int fd)
405 if (haptic->hwdata ==
NULL) {
409 SDL_memset(haptic->hwdata, 0,
sizeof(*haptic->hwdata));
412 haptic->hwdata->fd =
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",
422 haptic->nplaying = haptic->neffects;
425 if (haptic->effects ==
NULL) {
438 if (haptic->hwdata !=
NULL) {
440 haptic->hwdata =
NULL;
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);
471 haptic->hwdata->fname =
SDL_strdup( item->fname );
483 int device_index = 0;
486 for (item = SDL_hapticlist; item; item = item->
next) {
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);
527 if (
SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) {
540 int device_index = 0;
546 for (item = SDL_hapticlist; item; item = item->
next) {
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);
568 haptic->hwdata->fname =
SDL_strdup( joystick->hwdata->fname );
580 if (haptic->hwdata) {
584 haptic->effects =
NULL;
585 haptic->neffects = 0;
588 close(haptic->hwdata->fd);
593 haptic->hwdata =
NULL;
610 for (item = SDL_hapticlist; item; item =
next) {
619 SDL_UDEV_DelCallback(haptic_udev_callback);
624 SDL_hapticlist =
NULL;
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));
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);
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);
805 dest->u.periodic.magnitude = CLAMP(periodic->
magnitude) * 2;
806 dest->u.periodic.offset = periodic->
offset;
808 dest->u.periodic.phase = ((
Uint32)periodic->
phase * 0x10000U) / 36000;
811 dest->u.periodic.envelope.attack_length =
813 dest->u.periodic.envelope.attack_level =
815 dest->u.periodic.envelope.fade_length = CLAMP(periodic->
fade_length);
816 dest->u.periodic.envelope.fade_level = CLAMP(periodic->
fade_level);
828 dest->
type = FF_SPRING;
830 dest->type = FF_DAMPER;
832 dest->type = FF_INERTIA;
834 dest->type = FF_FRICTION;
839 0 : CLAMP(condition->
length);
840 dest->replay.delay = CLAMP(condition->
delay);
843 dest->trigger.button = SDL_SYS_ToButton(condition->
button);
844 dest->trigger.interval = CLAMP(condition->
interval);
848 dest->u.condition[0].right_saturation = condition->
right_sat[0];
849 dest->u.condition[0].left_saturation = condition->
left_sat[0];
850 dest->u.condition[0].right_coeff = condition->
right_coeff[0];
851 dest->u.condition[0].left_coeff = condition->
left_coeff[0];
852 dest->u.condition[0].deadband = condition->
deadband[0];
853 dest->u.condition[0].center = condition->
center[0];
855 dest->u.condition[1].right_saturation = condition->
right_sat[1];
856 dest->u.condition[1].left_saturation = condition->
left_sat[1];
857 dest->u.condition[1].right_coeff = condition->
right_coeff[1];
858 dest->u.condition[1].left_coeff = condition->
left_coeff[1];
859 dest->u.condition[1].deadband = condition->
deadband[1];
860 dest->u.condition[1].center = condition->
center[1];
872 dest->
type = FF_RAMP;
873 if (SDL_SYS_ToDirection(&dest->direction, &ramp->
direction) == -1)
879 dest->replay.delay = CLAMP(ramp->
delay);
882 dest->trigger.button = SDL_SYS_ToButton(ramp->
button);
883 dest->trigger.interval = CLAMP(ramp->
interval);
886 dest->u.ramp.start_level = ramp->
start;
887 dest->u.ramp.end_level = ramp->
end;
890 dest->u.ramp.envelope.attack_length = CLAMP(ramp->
attack_length);
891 dest->u.ramp.envelope.attack_level = CLAMP(ramp->
attack_level);
892 dest->u.ramp.envelope.fade_length = CLAMP(ramp->
fade_length);
893 dest->u.ramp.envelope.fade_level = CLAMP(ramp->
fade_level);
901 dest->
type = FF_RUMBLE;
906 0 : CLAMP(leftright->
length);
909 dest->trigger.button = 0;
910 dest->trigger.interval = 0;
913 dest->u.rumble.strong_magnitude = CLAMP(leftright->
large_magnitude) * 2;
934 struct ff_effect *linux_effect;
944 linux_effect = &effect->
hweffect->effect;
945 if (SDL_SYS_ToFFEffect(linux_effect, base) != 0) {
948 linux_effect->id = -1;
951 if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
952 SDL_SetError(
"Haptic: Error uploading effect to the device: %s",
977 struct ff_effect linux_effect;
980 if (SDL_SYS_ToFFEffect(&linux_effect, data) != 0) {
983 linux_effect.id = effect->
hweffect->effect.id;
986 if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
987 return SDL_SetError(
"Haptic: Error updating the effect: %s",
993 sizeof(
struct ff_effect));
1006 struct input_event run;
1010 run.code = effect->
hweffect->effect.id;
1012 run.value = (iterations > INT_MAX) ? INT_MAX : iterations;
1014 if (write(haptic->hwdata->fd, (
const void *) &run,
sizeof(run)) < 0) {
1015 return SDL_SetError(
"Haptic: Unable to run the effect: %s", strerror(errno));
1028 struct input_event stop;
1031 stop.code = effect->
hweffect->effect.id;
1034 if (write(haptic->hwdata->fd, (
const void *) &stop,
sizeof(stop)) < 0) {
1035 return SDL_SetError(
"Haptic: Unable to stop the effect: %s",
1049 if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->
hweffect->effect.id) < 0) {
1050 SDL_SetError(
"Haptic: Error removing the effect from the device: %s",
1066 struct input_event ie;
1069 ie.type = EV_FF_STATUS;
1070 ie.code = effect->
hweffect->effect.id;
1072 if (write(haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1073 return SDL_SetError(
"Haptic: Error getting device status.");
1089 struct input_event ie;
1093 ie.value = (0xFFFFUL * gain) / 100;
1095 if (write(haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1096 return SDL_SetError(
"Haptic: Error setting gain: %s", strerror(errno));
1109 struct input_event ie;
1112 ie.code = FF_AUTOCENTER;
1113 ie.value = (0xFFFFUL * autocenter) / 100;
1115 if (write(haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1116 return SDL_SetError(
"Haptic: Error setting autocenter: %s", strerror(errno));
1152 for (i = 0; i < haptic->neffects; i++) {
1153 if (haptic->effects[i].hweffect !=
NULL) {
1157 (
"Haptic: Error while trying to stop all playing effects.");
int SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
Structure that represents a haptic direction.
#define SDL_HAPTIC_SPHERICAL
Uses spherical coordinates for the direction.
#define SDL_HAPTIC_AUTOCENTER
Device can set autocenter.
A structure containing a template for a Periodic effect.
#define SDL_HAPTIC_GAIN
Device can set global gain.
#define SDL_HAPTIC_CUSTOM
Custom effect is supported.
#define SDL_HAPTIC_TRIANGLE
Triangle wave effect supported.
int SDL_SYS_HapticOpen(SDL_Haptic *haptic)
int SDL_SYS_HapticMouse(void)
int SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
#define SDL_HAPTIC_INERTIA
Inertia effect supported - uses axes acceleration.
int SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
const char * SDL_SYS_HapticName(int index)
A structure containing a template for a Condition effect.
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
The SDL haptic subsystem allows you to control haptic (force feedback) devices.
int SDL_SYS_HapticUnpause(SDL_Haptic *haptic)
int SDL_SYS_NumHaptics(void)
#define SDL_HAPTIC_SINE
Sine wave effect supported.
int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect)
A structure containing a template for a Constant effect.
#define SDL_HAPTIC_INFINITY
Used to play a device an infinite number of times.
#define SDL_HAPTIC_CARTESIAN
Uses cartesian coordinates for the direction.
SDL_hapticlist_item * SDL_hapticlist
struct SDL_hapticlist_item * next
SDL_HapticCondition condition
A structure containing a template for a Left/Right effect.
The generic template for any haptic effect.
#define SDL_HAPTIC_CONSTANT
Constant effect supported.
int SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
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)
#define SDL_HAPTIC_POLAR
Uses polar coordinates for the direction.
SDL_HapticConstant constant
int SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *data)
int SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
SDL_HapticDirection direction
A structure containing a template for a Ramp effect.
void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
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)
#define SDL_assert(condition)
#define SDL_OutOfMemory()
int SDL_SYS_HapticInit(void)
void SDL_SYS_HapticQuit(void)
struct haptic_hweffect * hweffect
void SDL_SYS_HapticClose(SDL_Haptic *haptic)
int SDL_SYS_HapticPause(SDL_Haptic *haptic)
SDL_HapticDirection direction
SDL_HapticLeftRight leftright
#define SDL_HAPTIC_RAMP
Ramp effect supported.
#define SDL_HAPTIC_SPRING
Spring effect supported - uses axes position.
int SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
#define SDL_HAPTIC_SAWTOOTHUP
Sawtoothup wave effect supported.
GLsizei const GLchar *const * path
int SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
#define SDL_HAPTIC_LEFTRIGHT
Left/Right effect supported.
int SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations)
SDL_HapticPeriodic periodic
#define SDL_HAPTIC_FRICTION
Friction effect supported - uses axes movement.
#define SDL_HAPTIC_SAWTOOTHDOWN
Sawtoothdown wave effect supported.
SDL_HapticDirection direction
int SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *base)
#define SDL_HAPTIC_DAMPER
Damper effect supported - uses axes velocity.