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
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 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
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 local 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
#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.