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 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 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.
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
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.
The SDL Haptic subsystem allows you to control haptic (force feedback) devices.
int SDL_SYS_HapticUnpause(SDL_Haptic *haptic)
uint32_t Uint32
An unsigned 32-bit integer type.
#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.
void * SDL_calloc(size_t nmemb, size_t size)
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
int32_t Sint32
A signed 32-bit integer type.
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.
uint16_t Uint16
An unsigned 16-bit integer type.
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.