21 #include "../../SDL_internal.h"
23 #ifdef SDL_HAPTIC_LINUX
26 #include "../SDL_syshaptic.h"
28 #include "../../joystick/SDL_sysjoystick.h"
29 #include "../../joystick/linux/SDL_sysjoystick_c.h"
30 #include "../../core/linux/SDL_evdev_capabilities.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);
80 struct haptic_hweffect
82 struct ff_effect effect;
87 static int numhaptics = 0;
89 #define EV_TEST(ev,f) \
90 if (test_bit((ev), features)) ret |= (f);
99 unsigned long features[1 + FF_MAX /
sizeof(
unsigned long)];
103 if (ioctl(
fd, EVIOCGBIT(EV_FF,
sizeof(features)), features) < 0) {
104 return SDL_SetError(
"Haptic: Unable to get device's features: %s",
137 unsigned long argp[40];
140 if (ioctl(
fd, EVIOCGBIT(EV_KEY,
sizeof(argp)), argp) < 0) {
145 if (test_bit(BTN_MOUSE, argp) != 0) {
158 const char joydev_pattern[] =
"/dev/input/event%d";
167 for (
j = 0;
j < MAX_HAPTICS; ++
j) {
169 snprintf(
path, PATH_MAX, joydev_pattern,
i++);
170 MaybeAddDevice(
path);
174 if (SDL_UDEV_Init() < 0) {
178 if ( SDL_UDEV_AddCallback(haptic_udev_callback) < 0) {
180 return SDL_SetError(
"Could not setup haptic <-> udev callback");
197 HapticByDevIndex(
int device_index)
201 if ((device_index < 0) || (device_index >= numhaptics)) {
205 while (device_index > 0) {
215 static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type,
int udev_class,
const char *devpath)
217 if (devpath ==
NULL || !(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
223 case SDL_UDEV_DEVICEADDED:
224 MaybeAddDevice(devpath);
227 case SDL_UDEV_DEVICEREMOVED:
228 MaybeRemoveDevice(devpath);
239 MaybeAddDevice(
const char *
path)
251 if (stat(
path, &sb) != 0) {
257 if (item->dev_num == sb.st_rdev) {
263 fd = open(
path, O_RDWR, 0);
268 #ifdef DEBUG_INPUT_EVENTS
269 printf(
"Checking %s\n",
path);
273 success = EV_IsHaptic(
fd);
285 if (item->fname ==
NULL) {
290 item->dev_num = sb.st_rdev;
293 if (SDL_hapticlist_tail ==
NULL) {
296 SDL_hapticlist_tail->
next = item;
297 SDL_hapticlist_tail = item;
309 MaybeRemoveDevice(
const char*
path)
329 if (item == SDL_hapticlist_tail) {
330 SDL_hapticlist_tail = prev;
352 SDL_SYS_HapticNameFromFD(
int fd)
354 static char namebuf[128];
357 if (ioctl(
fd, EVIOCGNAME(
sizeof(namebuf)), namebuf) <= 0) {
375 item = HapticByDevIndex(
index);
378 fd = open(item->fname, O_RDONLY, 0);
382 name = SDL_SYS_HapticNameFromFD(
fd);
398 SDL_SYS_HapticOpenFromFD(SDL_Haptic *
haptic,
int fd)
411 haptic->supported = EV_IsHaptic(
fd);
415 if (ioctl(
fd, EVIOCGEFFECTS, &
haptic->neffects) < 0) {
416 SDL_SetError(
"Haptic: Unable to query device memory: %s",
454 item = HapticByDevIndex(
haptic->index);
456 fd = open(item->fname, O_RDWR, 0);
459 item->fname, strerror(errno));
463 ret = SDL_SYS_HapticOpenFromFD(
haptic,
fd);
481 int device_index = 0;
486 fd = open(item->fname, O_RDWR, 0);
489 item->fname, strerror(errno));
493 if (EV_IsMouse(
fd)) {
513 #ifdef SDL_JOYSTICK_LINUX
517 if (EV_IsHaptic(
joystick->hwdata->fd)) {
531 #ifdef SDL_JOYSTICK_LINUX
551 #ifdef SDL_JOYSTICK_LINUX
552 int device_index = 0;
567 haptic->index = device_index;
569 if (device_index >= MAX_HAPTICS) {
570 return SDL_SetError(
"Haptic: Joystick doesn't have Haptic capabilities");
573 fd = open(
joystick->hwdata->fname, O_RDWR, 0);
576 joystick->hwdata->fname, strerror(errno));
578 ret = SDL_SYS_HapticOpenFromFD(
haptic,
fd);
606 close(
haptic->hwdata->fd);
637 SDL_UDEV_DelCallback(haptic_udev_callback);
643 SDL_hapticlist_tail =
NULL;
662 ff_button = BTN_GAMEPAD +
button - 1;
690 tmp = ((
src->dir[0] % 36000) * 0x8000) / 18000;
703 tmp = ((
src->dir[0]) + 9000) % 36000;
704 tmp = (tmp * 0x8000) / 18000;
710 *dest = (
src->dir[0] >= 0 ? 0x4000 : 0xC000);
711 else if (!
src->dir[0])
712 *dest = (
src->dir[1] >= 0 ? 0x8000 : 0);
725 tmp = (((
Sint32) (
f * 18000. / M_PI)) + 45000) % 36000;
726 tmp = (tmp * 0x8000) / 18000;
734 return SDL_SetError(
"Haptic: Unsupported direction type.");
741 #define CLAMP(x) (((x) > 32767) ? 32767 : x)
756 SDL_memset(dest, 0,
sizeof(
struct ff_effect));
760 constant = &
src->constant;
763 dest->
type = FF_CONSTANT;
764 if (SDL_SYS_ToDirection(&dest->direction, &constant->
direction) == -1)
769 0 : CLAMP(constant->
length);
770 dest->replay.delay = CLAMP(constant->
delay);
773 dest->trigger.button = SDL_SYS_ToButton(constant->
button);
774 dest->trigger.interval = CLAMP(constant->
interval);
777 dest->u.constant.level = constant->
level;
780 dest->u.constant.envelope.attack_length =
782 dest->u.constant.envelope.attack_level =
784 dest->u.constant.envelope.fade_length = CLAMP(constant->
fade_length);
785 dest->u.constant.envelope.fade_level = CLAMP(constant->
fade_level);
795 periodic = &
src->periodic;
798 dest->
type = FF_PERIODIC;
799 if (SDL_SYS_ToDirection(&dest->direction, &periodic->
direction) == -1)
804 0 : CLAMP(periodic->
length);
805 dest->replay.delay = CLAMP(periodic->
delay);
808 dest->trigger.button = SDL_SYS_ToButton(periodic->
button);
809 dest->trigger.interval = CLAMP(periodic->
interval);
813 dest->u.periodic.waveform = FF_SINE;
818 dest->u.periodic.waveform = FF_TRIANGLE;
820 dest->u.periodic.waveform = FF_SAW_UP;
822 dest->u.periodic.waveform = FF_SAW_DOWN;
823 dest->u.periodic.period = CLAMP(periodic->
period);
824 dest->u.periodic.magnitude = periodic->
magnitude;
825 dest->u.periodic.offset = periodic->
offset;
827 dest->u.periodic.phase = ((
Uint32)periodic->
phase * 0x10000U) / 36000;
830 dest->u.periodic.envelope.attack_length =
832 dest->u.periodic.envelope.attack_level =
834 dest->u.periodic.envelope.fade_length = CLAMP(periodic->
fade_length);
835 dest->u.periodic.envelope.fade_level = CLAMP(periodic->
fade_level);
847 dest->type = FF_SPRING;
849 dest->type = FF_DAMPER;
851 dest->type = FF_INERTIA;
853 dest->type = FF_FRICTION;
859 dest->replay.delay = CLAMP(
condition->delay);
862 dest->trigger.button = SDL_SYS_ToButton(
condition->button);
863 dest->trigger.interval = CLAMP(
condition->interval);
867 dest->u.condition[0].right_saturation =
condition->right_sat[0];
868 dest->u.condition[0].left_saturation =
condition->left_sat[0];
869 dest->u.condition[0].right_coeff =
condition->right_coeff[0];
870 dest->u.condition[0].left_coeff =
condition->left_coeff[0];
871 dest->u.condition[0].deadband =
condition->deadband[0];
872 dest->u.condition[0].center =
condition->center[0];
874 dest->u.condition[1].right_saturation =
condition->right_sat[1];
875 dest->u.condition[1].left_saturation =
condition->left_sat[1];
876 dest->u.condition[1].right_coeff =
condition->right_coeff[1];
877 dest->u.condition[1].left_coeff =
condition->left_coeff[1];
878 dest->u.condition[1].deadband =
condition->deadband[1];
879 dest->u.condition[1].center =
condition->center[1];
891 dest->
type = FF_RAMP;
892 if (SDL_SYS_ToDirection(&dest->direction, &ramp->
direction) == -1)
898 dest->replay.delay = CLAMP(ramp->
delay);
901 dest->trigger.button = SDL_SYS_ToButton(ramp->
button);
902 dest->trigger.interval = CLAMP(ramp->
interval);
905 dest->u.ramp.start_level = ramp->
start;
906 dest->u.ramp.end_level = ramp->
end;
909 dest->u.ramp.envelope.attack_length = CLAMP(ramp->
attack_length);
910 dest->u.ramp.envelope.attack_level = CLAMP(ramp->
attack_level);
911 dest->u.ramp.envelope.fade_length = CLAMP(ramp->
fade_length);
912 dest->u.ramp.envelope.fade_level = CLAMP(ramp->
fade_level);
917 leftright = &
src->leftright;
920 dest->
type = FF_RUMBLE;
925 0 : CLAMP(leftright->
length);
928 dest->trigger.button = 0;
929 dest->trigger.interval = 0;
932 dest->u.rumble.strong_magnitude = CLAMP(leftright->
large_magnitude) * 2;
953 struct ff_effect *linux_effect;
956 effect->
hweffect = (
struct haptic_hweffect *)
963 linux_effect = &effect->
hweffect->effect;
964 if (SDL_SYS_ToFFEffect(linux_effect,
base) != 0) {
967 linux_effect->id = -1;
970 if (ioctl(
haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
971 SDL_SetError(
"Haptic: Error uploading effect to the device: %s",
996 struct ff_effect linux_effect;
999 if (SDL_SYS_ToFFEffect(&linux_effect,
data) != 0) {
1002 linux_effect.id = effect->
hweffect->effect.id;
1005 if (ioctl(
haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
1006 return SDL_SetError(
"Haptic: Error updating the effect: %s",
1012 sizeof(
struct ff_effect));
1014 return effect->
hweffect->effect.id;
1025 struct input_event run;
1029 run.code = effect->
hweffect->effect.id;
1033 if (write(
haptic->hwdata->fd, (
const void *) &run,
sizeof(run)) < 0) {
1034 return SDL_SetError(
"Haptic: Unable to run the effect: %s", strerror(errno));
1047 struct input_event stop;
1050 stop.code = effect->
hweffect->effect.id;
1053 if (write(
haptic->hwdata->fd, (
const void *) &stop,
sizeof(stop)) < 0) {
1054 return SDL_SetError(
"Haptic: Unable to stop the effect: %s",
1068 if (ioctl(
haptic->hwdata->fd, EVIOCRMFF, effect->
hweffect->effect.id) < 0) {
1069 SDL_SetError(
"Haptic: Error removing the effect from the device: %s",
1085 struct input_event ie;
1088 ie.type = EV_FF_STATUS;
1089 ie.code = effect->
hweffect->effect.id;
1091 if (write(
haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1092 return SDL_SetError(
"Haptic: Error getting device status.");
1108 struct input_event ie;
1112 ie.value = (0xFFFFUL * gain) / 100;
1114 if (write(
haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1115 return SDL_SetError(
"Haptic: Error setting gain: %s", strerror(errno));
1128 struct input_event ie;
1131 ie.code = FF_AUTOCENTER;
1132 ie.value = (0xFFFFUL * autocenter) / 100;
1134 if (write(
haptic->hwdata->fd, &ie,
sizeof(ie)) < 0) {
1135 return SDL_SetError(
"Haptic: Error setting autocenter: %s", strerror(errno));
1171 for (
i = 0;
i <
haptic->neffects;
i++) {
1176 (
"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_STEERING_AXIS
Use this value to play an effect on the steering wheel axis. This provides better compatibility acros...
#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)
const char * SDL_SYS_HapticName(int index)
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)
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_JoystickDriver SDL_LINUX_JoystickDriver
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 in i)
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)
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d ®2 endm macro vzip8 reg2 vzip d d ®2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld fname[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1 beq endif SRC MASK if dst_r_bpp DST_R else add endif PF add sub src_basereg pixdeinterleave mask_basereg pixdeinterleave dst_r_basereg process_pixblock_head pixblock_size cache_preload_simple process_pixblock_tail pixinterleave dst_w_basereg irp beq endif process_pixblock_tail_head tst beq irp if pixblock_size chunk_size tst beq pixld_src SRC pixld MASK if DST_R else pixld DST_R endif if src_basereg pixdeinterleave mask_basereg pixdeinterleave dst_r_basereg process_pixblock_head if pixblock_size cache_preload_simple endif process_pixblock_tail pixinterleave dst_w_basereg irp if pixblock_size chunk_size tst beq if DST_W else pixst DST_W else mov ORIG_W endif add lsl if lsl endif if lsl endif lsl endif lsl endif lsl endif subs mov DST_W if regs_shortage str endif bge start_of_loop_label endm macro generate_composite_function
set set set set set set set set set set set set set set set set set set set set *set set set macro pixldst base
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
static SDL_Joystick * joystick
The generic template for any haptic effect.