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");
196 HapticByDevIndex(
int device_index)
200 if ((device_index < 0) || (device_index >= numhaptics)) {
204 while (device_index > 0) {
214 static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type,
int udev_class,
const char *devpath)
216 if (devpath ==
NULL || !(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
222 case SDL_UDEV_DEVICEADDED:
223 MaybeAddDevice(devpath);
226 case SDL_UDEV_DEVICEREMOVED:
227 MaybeRemoveDevice(devpath);
238 MaybeAddDevice(
const char *path)
250 if (stat(path, &sb) != 0) {
255 for (item = SDL_hapticlist; item !=
NULL; item = item->
next) {
256 if (item->dev_num == sb.st_rdev) {
262 fd = open(path, O_RDWR, 0);
267 #ifdef DEBUG_INPUT_EVENTS 268 printf(
"Checking %s\n", path);
272 success = EV_IsHaptic(fd);
284 if (item->fname ==
NULL) {
289 item->dev_num = sb.st_rdev;
292 if (SDL_hapticlist_tail ==
NULL) {
293 SDL_hapticlist = SDL_hapticlist_tail = item;
295 SDL_hapticlist_tail->
next = item;
296 SDL_hapticlist_tail = item;
308 MaybeRemoveDevice(
const char* path)
317 for (item = SDL_hapticlist; item !=
NULL; item = item->
next) {
326 SDL_hapticlist = item->
next;
328 if (item == SDL_hapticlist_tail) {
329 SDL_hapticlist_tail = prev;
351 SDL_SYS_HapticNameFromFD(
int fd)
353 static char namebuf[128];
356 if (ioctl(fd, EVIOCGNAME(
sizeof(namebuf)), namebuf) <= 0) {
374 item = HapticByDevIndex(index);
377 fd = open(item->fname, O_RDONLY, 0);
381 name = SDL_SYS_HapticNameFromFD(fd);
397 SDL_SYS_HapticOpenFromFD(SDL_Haptic *
haptic,
int fd)
402 if (haptic->hwdata ==
NULL) {
406 SDL_memset(haptic->hwdata, 0,
sizeof(*haptic->hwdata));
409 haptic->hwdata->fd =
fd;
410 haptic->supported = EV_IsHaptic(fd);
414 if (ioctl(fd, EVIOCGEFFECTS, &haptic->neffects) < 0) {
415 SDL_SetError(
"Haptic: Unable to query device memory: %s",
419 haptic->nplaying = haptic->neffects;
422 if (haptic->effects ==
NULL) {
435 if (haptic->hwdata !=
NULL) {
437 haptic->hwdata =
NULL;
453 item = HapticByDevIndex(haptic->index);
455 fd = open(item->fname, O_RDWR, 0);
458 item->fname, strerror(errno));
462 ret = SDL_SYS_HapticOpenFromFD(haptic, fd);
468 haptic->hwdata->fname =
SDL_strdup( item->fname );
480 int device_index = 0;
483 for (item = SDL_hapticlist; item; item = item->
next) {
485 fd = open(item->fname, O_RDWR, 0);
488 item->fname, strerror(errno));
492 if (EV_IsMouse(fd)) {
512 return EV_IsHaptic(joystick->hwdata->fd);
524 if (
SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) {
537 int device_index = 0;
543 for (item = SDL_hapticlist; item; item = item->
next) {
544 if (
SDL_strcmp(item->fname, joystick->hwdata->fname) == 0) {
549 haptic->index = device_index;
551 if (device_index >= MAX_HAPTICS) {
552 return SDL_SetError(
"Haptic: Joystick doesn't have Haptic capabilities");
555 fd = open(joystick->hwdata->fname, O_RDWR, 0);
558 joystick->hwdata->fname, strerror(errno));
560 ret = SDL_SYS_HapticOpenFromFD(haptic, fd);
565 haptic->hwdata->fname =
SDL_strdup( joystick->hwdata->fname );
577 if (haptic->hwdata) {
581 haptic->effects =
NULL;
582 haptic->neffects = 0;
585 close(haptic->hwdata->fd);
590 haptic->hwdata =
NULL;
607 for (item = SDL_hapticlist; item; item =
next) {
616 SDL_UDEV_DelCallback(haptic_udev_callback);
621 SDL_hapticlist =
NULL;
622 SDL_hapticlist_tail =
NULL;
641 ff_button = BTN_GAMEPAD + button - 1;
669 tmp = ((src->
dir[0] % 36000) * 0x8000) / 18000;
682 tmp = ((src->
dir[0]) + 9000) % 36000;
683 tmp = (tmp * 0x8000) / 18000;
689 *dest = (src->
dir[0] >= 0 ? 0x4000 : 0xC000);
690 else if (!src->
dir[0])
691 *dest = (src->
dir[1] >= 0 ? 0x8000 : 0);
704 tmp = (((
Sint32) (f * 18000. / M_PI)) + 45000) % 36000;
705 tmp = (tmp * 0x8000) / 18000;
711 return SDL_SetError(
"Haptic: Unsupported direction type.");
718 #define CLAMP(x) (((x) > 32767) ? 32767 : x) 733 SDL_memset(dest, 0,
sizeof(
struct ff_effect));
740 dest->
type = FF_CONSTANT;
741 if (SDL_SYS_ToDirection(&dest->direction, &constant->
direction) == -1)
746 0 : CLAMP(constant->
length);
747 dest->replay.delay = CLAMP(constant->
delay);
750 dest->trigger.button = SDL_SYS_ToButton(constant->
button);
751 dest->trigger.interval = CLAMP(constant->
interval);
754 dest->u.constant.level = constant->
level;
757 dest->u.constant.envelope.attack_length =
759 dest->u.constant.envelope.attack_level =
761 dest->u.constant.envelope.fade_length = CLAMP(constant->
fade_length);
762 dest->u.constant.envelope.fade_level = CLAMP(constant->
fade_level);
775 dest->
type = FF_PERIODIC;
776 if (SDL_SYS_ToDirection(&dest->direction, &periodic->
direction) == -1)
781 0 : CLAMP(periodic->
length);
782 dest->replay.delay = CLAMP(periodic->
delay);
785 dest->trigger.button = SDL_SYS_ToButton(periodic->
button);
786 dest->trigger.interval = CLAMP(periodic->
interval);
790 dest->u.periodic.waveform = FF_SINE;
795 dest->u.periodic.waveform = FF_TRIANGLE;
797 dest->u.periodic.waveform = FF_SAW_UP;
799 dest->u.periodic.waveform = FF_SAW_DOWN;
800 dest->u.periodic.period = CLAMP(periodic->
period);
801 dest->u.periodic.magnitude = periodic->
magnitude;
802 dest->u.periodic.offset = periodic->
offset;
804 dest->u.periodic.phase = ((
Uint32)periodic->
phase * 0x10000U) / 36000;
807 dest->u.periodic.envelope.attack_length =
809 dest->u.periodic.envelope.attack_level =
811 dest->u.periodic.envelope.fade_length = CLAMP(periodic->
fade_length);
812 dest->u.periodic.envelope.fade_level = CLAMP(periodic->
fade_level);
824 dest->
type = FF_SPRING;
826 dest->type = FF_DAMPER;
828 dest->type = FF_INERTIA;
830 dest->type = FF_FRICTION;
835 0 : CLAMP(condition->
length);
836 dest->replay.delay = CLAMP(condition->
delay);
839 dest->trigger.button = SDL_SYS_ToButton(condition->
button);
840 dest->trigger.interval = CLAMP(condition->
interval);
844 dest->u.condition[0].right_saturation = condition->
right_sat[0];
845 dest->u.condition[0].left_saturation = condition->
left_sat[0];
846 dest->u.condition[0].right_coeff = condition->
right_coeff[0];
847 dest->u.condition[0].left_coeff = condition->
left_coeff[0];
848 dest->u.condition[0].deadband = condition->
deadband[0];
849 dest->u.condition[0].center = condition->
center[0];
851 dest->u.condition[1].right_saturation = condition->
right_sat[1];
852 dest->u.condition[1].left_saturation = condition->
left_sat[1];
853 dest->u.condition[1].right_coeff = condition->
right_coeff[1];
854 dest->u.condition[1].left_coeff = condition->
left_coeff[1];
855 dest->u.condition[1].deadband = condition->
deadband[1];
856 dest->u.condition[1].center = condition->
center[1];
868 dest->
type = FF_RAMP;
869 if (SDL_SYS_ToDirection(&dest->direction, &ramp->
direction) == -1)
875 dest->replay.delay = CLAMP(ramp->
delay);
878 dest->trigger.button = SDL_SYS_ToButton(ramp->
button);
879 dest->trigger.interval = CLAMP(ramp->
interval);
882 dest->u.ramp.start_level = ramp->
start;
883 dest->u.ramp.end_level = ramp->
end;
886 dest->u.ramp.envelope.attack_length = CLAMP(ramp->
attack_length);
887 dest->u.ramp.envelope.attack_level = CLAMP(ramp->
attack_level);
888 dest->u.ramp.envelope.fade_length = CLAMP(ramp->
fade_length);
889 dest->u.ramp.envelope.fade_level = CLAMP(ramp->
fade_level);
897 dest->
type = FF_RUMBLE;
902 0 : CLAMP(leftright->
length);
905 dest->trigger.button = 0;
906 dest->trigger.interval = 0;
930 struct ff_effect *linux_effect;
940 linux_effect = &effect->
hweffect->effect;
941 if (SDL_SYS_ToFFEffect(linux_effect, base) != 0) {
944 linux_effect->id = -1;
947 if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
948 SDL_SetError(
"Haptic: Error uploading effect to the device: %s",
973 struct ff_effect linux_effect;
976 if (SDL_SYS_ToFFEffect(&linux_effect, data) != 0) {
979 linux_effect.id = effect->
hweffect->effect.id;
982 if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
983 return SDL_SetError(
"Haptic: Error updating the effect: %s",
989 sizeof(
struct ff_effect));
1002 struct input_event run;
1006 run.code = effect->
hweffect->effect.id;
1008 run.value = (iterations > INT_MAX) ? INT_MAX : iterations;
1010 if (write(haptic->hwdata->fd, (
const void *) &run,
sizeof(run)) < 0) {
1011 return SDL_SetError(
"Haptic: Unable to run the effect: %s", strerror(errno));
1024 struct input_event stop;
1027 stop.code = effect->
hweffect->effect.id;
1030 if (write(haptic->hwdata->fd, (
const void *) &stop,
sizeof(stop)) < 0) {
1031 return SDL_SetError(
"Haptic: Unable to stop the effect: %s",
1045 if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->
hweffect->effect.id) < 0) {
1046 SDL_SetError(
"Haptic: Error removing the effect from the device: %s",
1062 struct input_event ie;
1065 ie.type = EV_FF_STATUS;
1066 ie.code = effect->
hweffect->effect.id;
1068 if (write(haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1069 return SDL_SetError(
"Haptic: Error getting device status.");
1085 struct input_event ie;
1089 ie.value = (0xFFFFUL * gain) / 100;
1091 if (write(haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1092 return SDL_SetError(
"Haptic: Error setting gain: %s", strerror(errno));
1105 struct input_event ie;
1108 ie.code = FF_AUTOCENTER;
1109 ie.value = (0xFFFFUL * autocenter) / 100;
1111 if (write(haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1112 return SDL_SetError(
"Haptic: Error setting autocenter: %s", strerror(errno));
1148 for (i = 0; i < haptic->neffects; i++) {
1149 if (haptic->effects[i].hweffect !=
NULL) {
1153 (
"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.