23 #ifdef SDL_HAPTIC_LINUX
26 #include "../SDL_syshaptic.h"
28 #include "../../joystick/SDL_sysjoystick.h"
29 #include "../../joystick/linux/SDL_sysjoystick_c.h"
32 #include <linux/input.h>
41 # define M_PI 3.14159265358979323846
45 #define MAX_HAPTICS 32
55 } SDL_hapticlist[MAX_HAPTICS];
71 struct haptic_hweffect
73 struct ff_effect effect;
78 #define test_bit(nr, addr) \
79 (((1UL << ((nr) & 31)) & (((const unsigned int *) addr)[(nr) >> 5])) != 0)
80 #define EV_TEST(ev,f) \
81 if (test_bit((ev), features)) ret |= (f);
90 unsigned long features[1 + FF_MAX /
sizeof(
unsigned long)];
94 if (ioctl(fd, EVIOCGBIT(EV_FF,
sizeof(features)), features) < 0) {
95 return SDL_SetError(
"Haptic: Unable to get device's features: %s",
128 unsigned long argp[40];
131 if (ioctl(fd, EVIOCGBIT(EV_KEY,
sizeof(argp)), argp) < 0) {
136 if (test_bit(BTN_MOUSE, argp) != 0) {
149 const char joydev_pattern[] =
"/dev/input/event%d";
150 dev_t dev_nums[MAX_HAPTICS];
165 for (j = 0; j < MAX_HAPTICS; ++
j) {
167 snprintf(path,
PATH_MAX, joydev_pattern, i++);
170 if (stat(path, &sb) != 0)
175 for (k = 0; (k < numhaptics) && !duplicate; ++
k) {
176 if (sb.st_rdev == dev_nums[k]) {
185 fd = open(path, O_RDWR, 0);
189 #ifdef DEBUG_INPUT_EVENTS
190 printf(
"Checking %s\n", path);
194 if (EV_IsHaptic(fd) > 0) {
195 SDL_hapticlist[numhaptics].fname =
SDL_strdup(path);
196 SDL_hapticlist[numhaptics].haptic =
NULL;
197 dev_nums[numhaptics] = sb.st_rdev;
211 SDL_SYS_HapticNameFromFD(
int fd)
213 static char namebuf[128];
216 if (ioctl(fd, EVIOCGNAME(
sizeof(namebuf)), namebuf) <= 0) {
235 fd = open(SDL_hapticlist[index].fname, O_RDONLY, 0);
239 name = SDL_SYS_HapticNameFromFD(fd);
242 name = SDL_hapticlist[
index].fname;
255 SDL_SYS_HapticOpenFromFD(
SDL_Haptic * haptic,
int fd)
258 haptic->
hwdata = (
struct haptic_hwdata *)
272 if (ioctl(fd, EVIOCGEFFECTS, &haptic->
neffects) < 0) {
273 SDL_SetError(
"Haptic: Unable to query device memory: %s",
311 fd = open(SDL_hapticlist[haptic->
index].fname, O_RDWR, 0);
314 SDL_hapticlist[haptic->
index], strerror(errno));
318 ret = SDL_SYS_HapticOpenFromFD(haptic, fd);
324 haptic->
hwdata->fname = SDL_hapticlist[haptic->
index].fname;
341 fd = open(SDL_hapticlist[i].fname, O_RDWR, 0);
344 SDL_hapticlist[i], strerror(errno));
348 if (EV_IsMouse(fd)) {
366 return EV_IsHaptic(joystick->
hwdata->
fd);
397 for (i = 0; i < MAX_HAPTICS; i++) {
398 if (SDL_hapticlist[i].fname !=
NULL) {
406 if (i >= MAX_HAPTICS) {
407 return SDL_SetError(
"Haptic: Joystick doesn't have Haptic capabilities");
415 ret = SDL_SYS_HapticOpenFromFD(haptic, fd);
420 haptic->
hwdata->fname = SDL_hapticlist[haptic->
index].fname;
439 close(haptic->
hwdata->fd);
459 for (i = 0; SDL_hapticlist[
i].fname !=
NULL; i++) {
465 SDL_hapticlist[0].fname =
NULL;
473 SDL_SYS_ToButton(
Uint16 button)
484 ff_button = BTN_GAMEPAD + button - 1;
511 tmp = (((18000 + dir->
dir[0]) % 36000) * 0xFFFF) / 36000;
523 tmp = ((dir->
dir[0]) + 9000) % 36000;
524 tmp = (((18000 + tmp) % 36000) * 0xFFFF) / 36000;
528 f = atan2(dir->
dir[1], dir->
dir[0]);
539 tmp = (((
int) (f * 18000. /
M_PI)) + 45000) % 36000;
540 tmp = (((18000 + tmp) % 36000) * 0xFFFF) / 36000;
551 #define CLAMP(x) (((x) > 32767) ? 32767 : x)
567 SDL_memset(dest, 0,
sizeof(
struct ff_effect));
574 dest->
type = FF_CONSTANT;
575 dest->direction = SDL_SYS_ToDirection(&constant->
direction);
576 if (dest->direction == (
Uint16) - 1)
581 0 : CLAMP(constant->
length);
582 dest->replay.delay = CLAMP(constant->
delay);
585 dest->trigger.button = SDL_SYS_ToButton(constant->
button);
586 dest->trigger.interval = CLAMP(constant->
interval);
589 dest->u.constant.level = constant->
level;
592 dest->u.constant.envelope.attack_length =
594 dest->u.constant.envelope.attack_level =
596 dest->u.constant.envelope.fade_length = CLAMP(constant->
fade_length);
597 dest->u.constant.envelope.fade_level = CLAMP(constant->
fade_level);
610 dest->
type = FF_PERIODIC;
611 dest->direction = SDL_SYS_ToDirection(&periodic->
direction);
612 if (dest->direction == (
Uint16) - 1)
617 0 : CLAMP(periodic->
length);
618 dest->replay.delay = CLAMP(periodic->
delay);
621 dest->trigger.button = SDL_SYS_ToButton(periodic->
button);
622 dest->trigger.interval = CLAMP(periodic->
interval);
626 dest->u.periodic.waveform = FF_SINE;
631 dest->u.periodic.waveform = FF_TRIANGLE;
633 dest->u.periodic.waveform = FF_SAW_UP;
635 dest->u.periodic.waveform = FF_SAW_DOWN;
636 dest->u.periodic.period = CLAMP(periodic->
period);
637 dest->u.periodic.magnitude = periodic->
magnitude;
638 dest->u.periodic.offset = periodic->
offset;
640 tmp = ((periodic->
phase % 36000) * dest->u.periodic.period) / 36000;
641 dest->u.periodic.phase = CLAMP(tmp);
644 dest->u.periodic.envelope.attack_length =
646 dest->u.periodic.envelope.attack_level =
648 dest->u.periodic.envelope.fade_length = CLAMP(periodic->
fade_length);
649 dest->u.periodic.envelope.fade_level = CLAMP(periodic->
fade_level);
661 dest->
type = FF_SPRING;
663 dest->type = FF_DAMPER;
665 dest->type = FF_INERTIA;
667 dest->type = FF_FRICTION;
672 0 : CLAMP(condition->
length);
673 dest->replay.delay = CLAMP(condition->
delay);
676 dest->trigger.button = SDL_SYS_ToButton(condition->
button);
677 dest->trigger.interval = CLAMP(condition->
interval);
681 dest->u.condition[0].right_saturation =
683 dest->u.condition[0].left_saturation = CLAMP(condition->
left_sat[0]);
684 dest->u.condition[0].right_coeff = condition->
right_coeff[0];
685 dest->u.condition[0].left_coeff = condition->
left_coeff[0];
686 dest->u.condition[0].deadband = CLAMP(condition->
deadband[0]);
687 dest->u.condition[0].center = condition->
center[0];
689 dest->u.condition[1].right_saturation =
691 dest->u.condition[1].left_saturation = CLAMP(condition->
left_sat[1]);
692 dest->u.condition[1].right_coeff = condition->
right_coeff[1];
693 dest->u.condition[1].left_coeff = condition->
left_coeff[1];
694 dest->u.condition[1].deadband = CLAMP(condition->
deadband[1]);
695 dest->u.condition[1].center = condition->
center[1];
707 dest->
type = FF_RAMP;
708 dest->direction = SDL_SYS_ToDirection(&ramp->
direction);
709 if (dest->direction == (
Uint16) - 1)
715 dest->replay.delay = CLAMP(ramp->
delay);
718 dest->trigger.button = SDL_SYS_ToButton(ramp->
button);
719 dest->trigger.interval = CLAMP(ramp->
interval);
722 dest->u.ramp.start_level = ramp->
start;
723 dest->u.ramp.end_level = ramp->
end;
726 dest->u.ramp.envelope.attack_length = CLAMP(ramp->
attack_length);
727 dest->u.ramp.envelope.attack_level = CLAMP(ramp->
attack_level);
728 dest->u.ramp.envelope.fade_length = CLAMP(ramp->
fade_length);
729 dest->u.ramp.envelope.fade_level = CLAMP(ramp->
fade_level);
737 dest->
type = FF_RUMBLE;
742 0 : CLAMP(leftright->
length);
745 dest->trigger.button = 0;
746 dest->trigger.interval = 0;
770 struct ff_effect *linux_effect;
773 effect->
hweffect = (
struct haptic_hweffect *)
780 linux_effect = &effect->
hweffect->effect;
781 if (SDL_SYS_ToFFEffect(linux_effect, base) != 0) {
784 linux_effect->id = -1;
787 if (ioctl(haptic->
hwdata->fd, EVIOCSFF, linux_effect) < 0) {
788 SDL_SetError(
"Haptic: Error uploading effect to the device: %s",
813 struct ff_effect linux_effect;
816 if (SDL_SYS_ToFFEffect(&linux_effect, data) != 0) {
819 linux_effect.id = effect->
hweffect->effect.id;
822 if (ioctl(haptic->
hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
823 return SDL_SetError(
"Haptic: Error updating the effect: %s",
829 sizeof(
struct ff_effect));
842 struct input_event run;
846 run.code = effect->
hweffect->effect.id;
848 run.value = (iterations > INT_MAX) ? INT_MAX : iterations;
850 if (write(haptic->
hwdata->fd, (
const void *) &run,
sizeof(run)) < 0) {
851 return SDL_SetError(
"Haptic: Unable to run the effect: %s", strerror(errno));
864 struct input_event stop;
867 stop.code = effect->
hweffect->effect.id;
870 if (write(haptic->
hwdata->fd, (
const void *) &stop,
sizeof(stop)) < 0) {
871 return SDL_SetError(
"Haptic: Unable to stop the effect: %s",
886 SDL_SetError(
"Haptic: Error removing the effect from the device: %s",
902 struct input_event ie;
905 ie.type = EV_FF_STATUS;
906 ie.code = effect->
hweffect->effect.id;
908 if (write(haptic->
hwdata->fd, &ie,
sizeof(ie)) < 0) {
909 return SDL_SetError(
"Haptic: Error getting device status.");
925 struct input_event ie;
929 ie.value = (0xFFFFUL * gain) / 100;
931 if (write(haptic->
hwdata->fd, &ie,
sizeof(ie)) < 0) {
932 return SDL_SetError(
"Haptic: Error setting gain: %s", strerror(errno));
945 struct input_event ie;
948 ie.code = FF_AUTOCENTER;
949 ie.value = (0xFFFFUL * autocenter) / 100;
951 if (write(haptic->
hwdata->fd, &ie,
sizeof(ie)) < 0) {
952 return SDL_SetError(
"Haptic: Error setting autocenter: %s", strerror(errno));
988 for (i = 0; i < haptic->
neffects; i++) {
993 (
"Haptic: Error while trying to stop all playing effects.");
int SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
struct haptic_hwdata * hwdata
Structure that represents a haptic direction.
struct haptic_effect * effects
#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.
DECLSPEC void SDLCALL SDL_free(void *mem)
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.
EGLImageKHR EGLint * name
The SDL Haptic subsystem allows you to control haptic (force feedback) devices.
int SDL_SYS_HapticUnpause(SDL_Haptic *haptic)
#define SDL_HAPTIC_SINE
Sine wave effect supported.
GLsizei const GLchar *const * path
int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect)
struct joystick_hwdata * hwdata
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.
uint32_t Uint32
An unsigned 32-bit integer type.
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)
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
DECLSPEC char *SDLCALL SDL_strdup(const char *str)
#define SDL_HAPTIC_POLAR
Uses polar coordinates for the direction.
DECLSPEC void *SDLCALL SDL_memset(void *dst, int c, size_t len)
SDL_HapticConstant constant
DECLSPEC int SDLCALL SDL_SetError(const char *fmt,...)
DECLSPEC void *SDLCALL SDL_malloc(size_t size)
int SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *data)
DECLSPEC int SDLCALL SDL_strcmp(const char *str1, const char *str2)
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)
#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)
DECLSPEC void *SDLCALL SDL_memcpy(void *dst, const void *src, size_t len)
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)
uint16_t Uint16
An unsigned 16-bit integer type.
#define SDL_HAPTIC_SAWTOOTHUP
Sawtoothup wave effect supported.
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.