Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/haptic/linux/SDL_syshaptic.c
9912 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
4
5
This software is provided 'as-is', without any express or implied
6
warranty. In no event will the authors be held liable for any damages
7
arising from the use of this software.
8
9
Permission is granted to anyone to use this software for any purpose,
10
including commercial applications, and to alter it and redistribute it
11
freely, subject to the following restrictions:
12
13
1. The origin of this software must not be misrepresented; you must not
14
claim that you wrote the original software. If you use this software
15
in a product, an acknowledgment in the product documentation would be
16
appreciated but is not required.
17
2. Altered source versions must be plainly marked as such, and must not be
18
misrepresented as being the original software.
19
3. This notice may not be removed or altered from any source distribution.
20
*/
21
#include "SDL_internal.h"
22
23
#ifdef SDL_HAPTIC_LINUX
24
25
#include "../SDL_syshaptic.h"
26
#include "../../joystick/SDL_sysjoystick.h" // For the real SDL_Joystick
27
#include "../../joystick/linux/SDL_sysjoystick_c.h" // For joystick hwdata
28
#include "../../core/linux/SDL_evdev_capabilities.h"
29
#include "../../core/linux/SDL_udev.h"
30
31
#include <unistd.h> // close
32
#include <linux/input.h> // Force feedback linux stuff.
33
#include <fcntl.h> // O_RDWR
34
#include <limits.h> // INT_MAX
35
#include <errno.h> // errno
36
#include <string.h> // strerror
37
#include <sys/stat.h> // stat
38
39
#define MAX_HAPTICS 32 // It's doubtful someone has more then 32 evdev
40
41
static bool MaybeAddDevice(const char *path);
42
#ifdef SDL_USE_LIBUDEV
43
static bool MaybeRemoveDevice(const char *path);
44
static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
45
#endif // SDL_USE_LIBUDEV
46
47
/*
48
* List of available haptic devices.
49
*/
50
typedef struct SDL_hapticlist_item
51
{
52
SDL_HapticID instance_id;
53
char *fname; // Dev path name (like /dev/input/event1)
54
SDL_Haptic *haptic; // Associated haptic.
55
dev_t dev_num;
56
struct SDL_hapticlist_item *next;
57
} SDL_hapticlist_item;
58
59
/*
60
* Haptic system hardware data.
61
*/
62
struct haptic_hwdata
63
{
64
int fd; // File descriptor of the device.
65
char *fname; // Points to the name in SDL_hapticlist.
66
};
67
68
/*
69
* Haptic system effect data.
70
*/
71
struct haptic_hweffect
72
{
73
struct ff_effect effect; // The linux kernel effect structure.
74
};
75
76
static SDL_hapticlist_item *SDL_hapticlist = NULL;
77
static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
78
static int numhaptics = 0;
79
80
#define EV_TEST(ev, f) \
81
if (test_bit((ev), features)) { \
82
ret |= (f); \
83
}
84
/*
85
* Test whether a device has haptic properties.
86
* Returns available properties or 0 if there are none.
87
*/
88
static Uint32 EV_IsHaptic(int fd)
89
{
90
unsigned long features[1 + FF_MAX / sizeof(unsigned long)];
91
Uint32 ret = 0;
92
93
// Ask device for what it has.
94
if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features) < 0) {
95
SDL_SetError("Haptic: Unable to get device's features: %s", strerror(errno));
96
return 0;
97
}
98
99
// Convert supported features to SDL_HAPTIC platform-neutral features.
100
EV_TEST(FF_CONSTANT, SDL_HAPTIC_CONSTANT);
101
EV_TEST(FF_SINE, SDL_HAPTIC_SINE);
102
EV_TEST(FF_SQUARE, SDL_HAPTIC_SQUARE);
103
EV_TEST(FF_TRIANGLE, SDL_HAPTIC_TRIANGLE);
104
EV_TEST(FF_SAW_UP, SDL_HAPTIC_SAWTOOTHUP);
105
EV_TEST(FF_SAW_DOWN, SDL_HAPTIC_SAWTOOTHDOWN);
106
EV_TEST(FF_RAMP, SDL_HAPTIC_RAMP);
107
EV_TEST(FF_SPRING, SDL_HAPTIC_SPRING);
108
EV_TEST(FF_FRICTION, SDL_HAPTIC_FRICTION);
109
EV_TEST(FF_DAMPER, SDL_HAPTIC_DAMPER);
110
EV_TEST(FF_INERTIA, SDL_HAPTIC_INERTIA);
111
EV_TEST(FF_CUSTOM, SDL_HAPTIC_CUSTOM);
112
EV_TEST(FF_GAIN, SDL_HAPTIC_GAIN);
113
EV_TEST(FF_AUTOCENTER, SDL_HAPTIC_AUTOCENTER);
114
EV_TEST(FF_RUMBLE, SDL_HAPTIC_LEFTRIGHT);
115
116
// Return what it supports.
117
return ret;
118
}
119
120
/*
121
* Tests whether a device is a mouse or not.
122
*/
123
static bool EV_IsMouse(int fd)
124
{
125
unsigned long argp[40];
126
127
// Ask for supported features.
128
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(argp)), argp) < 0) {
129
return false;
130
}
131
132
// Currently we only test for BTN_MOUSE which can give fake positives.
133
if (test_bit(BTN_MOUSE, argp) != 0) {
134
return true;
135
}
136
137
return true;
138
}
139
140
/*
141
* Initializes the haptic subsystem by finding available devices.
142
*/
143
bool SDL_SYS_HapticInit(void)
144
{
145
const char joydev_pattern[] = "/dev/input/event%d";
146
char path[PATH_MAX];
147
int i, j;
148
149
/*
150
* Limit amount of checks to MAX_HAPTICS since we may or may not have
151
* permission to some or all devices.
152
*/
153
i = 0;
154
for (j = 0; j < MAX_HAPTICS; ++j) {
155
(void)SDL_snprintf(path, PATH_MAX, joydev_pattern, i++);
156
MaybeAddDevice(path);
157
}
158
159
#ifdef SDL_USE_LIBUDEV
160
if (!SDL_UDEV_Init()) {
161
return SDL_SetError("Could not initialize UDEV");
162
}
163
164
if (!SDL_UDEV_AddCallback(haptic_udev_callback)) {
165
SDL_UDEV_Quit();
166
return SDL_SetError("Could not setup haptic <-> udev callback");
167
}
168
169
// Force a scan to build the initial device list
170
SDL_UDEV_Scan();
171
#endif // SDL_USE_LIBUDEV
172
173
return true;
174
}
175
176
int SDL_SYS_NumHaptics(void)
177
{
178
return numhaptics;
179
}
180
181
static SDL_hapticlist_item *HapticByDevIndex(int device_index)
182
{
183
SDL_hapticlist_item *item = SDL_hapticlist;
184
185
if ((device_index < 0) || (device_index >= numhaptics)) {
186
return NULL;
187
}
188
189
while (device_index > 0) {
190
SDL_assert(item != NULL);
191
--device_index;
192
item = item->next;
193
}
194
195
return item;
196
}
197
198
static SDL_hapticlist_item *HapticByInstanceID(SDL_HapticID instance_id)
199
{
200
SDL_hapticlist_item *item;
201
for (item = SDL_hapticlist; item; item = item->next) {
202
if (instance_id == item->instance_id) {
203
return item;
204
}
205
}
206
return NULL;
207
}
208
209
#ifdef SDL_USE_LIBUDEV
210
static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
211
{
212
if (!devpath || !(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
213
return;
214
}
215
216
switch (udev_type) {
217
case SDL_UDEV_DEVICEADDED:
218
MaybeAddDevice(devpath);
219
break;
220
221
case SDL_UDEV_DEVICEREMOVED:
222
MaybeRemoveDevice(devpath);
223
break;
224
225
default:
226
break;
227
}
228
}
229
#endif // SDL_USE_LIBUDEV
230
231
static bool MaybeAddDevice(const char *path)
232
{
233
struct stat sb;
234
int fd;
235
Uint32 supported;
236
SDL_hapticlist_item *item;
237
238
if (!path) {
239
return false;
240
}
241
242
// try to open
243
fd = open(path, O_RDWR | O_CLOEXEC, 0);
244
if (fd < 0) {
245
return false;
246
}
247
248
// get file status
249
if (fstat(fd, &sb) != 0) {
250
close(fd);
251
return false;
252
}
253
254
// check for duplicates
255
for (item = SDL_hapticlist; item; item = item->next) {
256
if (item->dev_num == sb.st_rdev) {
257
close(fd);
258
return false; // duplicate.
259
}
260
}
261
262
#ifdef DEBUG_INPUT_EVENTS
263
printf("Checking %s\n", path);
264
#endif
265
266
// see if it works
267
supported = EV_IsHaptic(fd);
268
close(fd);
269
if (!supported) {
270
return false;
271
}
272
273
item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item));
274
if (!item) {
275
return false;
276
}
277
278
item->instance_id = SDL_GetNextObjectID();
279
item->fname = SDL_strdup(path);
280
if (!item->fname) {
281
SDL_free(item);
282
return false;
283
}
284
285
item->dev_num = sb.st_rdev;
286
287
// TODO: should we add instance IDs?
288
if (!SDL_hapticlist_tail) {
289
SDL_hapticlist = SDL_hapticlist_tail = item;
290
} else {
291
SDL_hapticlist_tail->next = item;
292
SDL_hapticlist_tail = item;
293
}
294
295
++numhaptics;
296
297
// !!! TODO: Send a haptic add event?
298
299
return true;
300
}
301
302
#ifdef SDL_USE_LIBUDEV
303
static bool MaybeRemoveDevice(const char *path)
304
{
305
SDL_hapticlist_item *item;
306
SDL_hapticlist_item *prev = NULL;
307
308
if (!path) {
309
return false;
310
}
311
312
for (item = SDL_hapticlist; item; item = item->next) {
313
// found it, remove it.
314
if (SDL_strcmp(path, item->fname) == 0) {
315
const bool result = item->haptic ? true : false;
316
317
if (prev) {
318
prev->next = item->next;
319
} else {
320
SDL_assert(SDL_hapticlist == item);
321
SDL_hapticlist = item->next;
322
}
323
if (item == SDL_hapticlist_tail) {
324
SDL_hapticlist_tail = prev;
325
}
326
327
// Need to decrement the haptic count
328
--numhaptics;
329
// !!! TODO: Send a haptic remove event?
330
331
SDL_free(item->fname);
332
SDL_free(item);
333
return result;
334
}
335
prev = item;
336
}
337
338
return false;
339
}
340
#endif // SDL_USE_LIBUDEV
341
342
/*
343
* Return the instance ID of a haptic device, does not need to be opened.
344
*/
345
SDL_HapticID SDL_SYS_HapticInstanceID(int index)
346
{
347
SDL_hapticlist_item *item;
348
349
item = HapticByDevIndex(index);
350
if (item) {
351
return item->instance_id;
352
}
353
return 0;
354
}
355
356
/*
357
* Gets the name from a file descriptor.
358
*/
359
static const char *SDL_SYS_HapticNameFromFD(int fd)
360
{
361
static char namebuf[128];
362
363
// We use the evdev name ioctl.
364
if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) {
365
return NULL;
366
}
367
368
return namebuf;
369
}
370
371
/*
372
* Return the name of a haptic device, does not need to be opened.
373
*/
374
const char *SDL_SYS_HapticName(int index)
375
{
376
SDL_hapticlist_item *item;
377
int fd;
378
const char *name = NULL;
379
380
item = HapticByDevIndex(index);
381
if (item) {
382
// Open the haptic device.
383
fd = open(item->fname, O_RDONLY | O_CLOEXEC, 0);
384
385
if (fd >= 0) {
386
387
name = SDL_SYS_HapticNameFromFD(fd);
388
if (!name) {
389
// No name found, return device character device
390
name = item->fname;
391
}
392
close(fd);
393
}
394
}
395
return name;
396
}
397
398
/*
399
* Opens the haptic device from the file descriptor.
400
*/
401
static bool SDL_SYS_HapticOpenFromFD(SDL_Haptic *haptic, int fd)
402
{
403
// Allocate the hwdata
404
haptic->hwdata = (struct haptic_hwdata *)
405
SDL_calloc(1, sizeof(*haptic->hwdata));
406
if (!haptic->hwdata) {
407
goto open_err;
408
}
409
410
// Set the data.
411
haptic->hwdata->fd = fd;
412
haptic->supported = EV_IsHaptic(fd);
413
haptic->naxes = 2; // Hardcoded for now, not sure if it's possible to find out.
414
415
// Set the effects
416
if (ioctl(fd, EVIOCGEFFECTS, &haptic->neffects) < 0) {
417
SDL_SetError("Haptic: Unable to query device memory: %s",
418
strerror(errno));
419
goto open_err;
420
}
421
haptic->nplaying = haptic->neffects; // Linux makes no distinction.
422
haptic->effects = (struct haptic_effect *)
423
SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
424
if (!haptic->effects) {
425
goto open_err;
426
}
427
// Clear the memory
428
SDL_memset(haptic->effects, 0,
429
sizeof(struct haptic_effect) * haptic->neffects);
430
431
return true;
432
433
// Error handling
434
open_err:
435
close(fd);
436
if (haptic->hwdata) {
437
SDL_free(haptic->hwdata);
438
haptic->hwdata = NULL;
439
}
440
return false;
441
}
442
443
/*
444
* Opens a haptic device for usage.
445
*/
446
bool SDL_SYS_HapticOpen(SDL_Haptic *haptic)
447
{
448
int fd;
449
SDL_hapticlist_item *item;
450
451
item = HapticByInstanceID(haptic->instance_id);
452
// Open the character device
453
fd = open(item->fname, O_RDWR | O_CLOEXEC, 0);
454
if (fd < 0) {
455
return SDL_SetError("Haptic: Unable to open %s: %s",
456
item->fname, strerror(errno));
457
}
458
459
// Try to create the haptic.
460
if (!SDL_SYS_HapticOpenFromFD(haptic, fd)) {
461
// Already closes on error.
462
return false;
463
}
464
465
// Set the fname.
466
haptic->hwdata->fname = SDL_strdup(item->fname);
467
return true;
468
}
469
470
/*
471
* Opens a haptic device from first mouse it finds for usage.
472
*/
473
int SDL_SYS_HapticMouse(void)
474
{
475
int fd;
476
int device_index = 0;
477
SDL_hapticlist_item *item;
478
479
for (item = SDL_hapticlist; item; item = item->next) {
480
// Open the device.
481
fd = open(item->fname, O_RDWR | O_CLOEXEC, 0);
482
if (fd < 0) {
483
return SDL_SetError("Haptic: Unable to open %s: %s",
484
item->fname, strerror(errno));
485
}
486
487
// Is it a mouse?
488
if (EV_IsMouse(fd)) {
489
close(fd);
490
return device_index;
491
}
492
493
close(fd);
494
495
++device_index;
496
}
497
498
return -1;
499
}
500
501
/*
502
* Checks to see if a joystick has haptic features.
503
*/
504
bool SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
505
{
506
#ifdef SDL_JOYSTICK_LINUX
507
SDL_AssertJoysticksLocked();
508
509
if (joystick->driver != &SDL_LINUX_JoystickDriver) {
510
return false;
511
}
512
if (EV_IsHaptic(joystick->hwdata->fd)) {
513
return true;
514
}
515
#endif
516
return false;
517
}
518
519
/*
520
* Checks to see if the haptic device and joystick are in reality the same.
521
*/
522
bool SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
523
{
524
#ifdef SDL_JOYSTICK_LINUX
525
SDL_AssertJoysticksLocked();
526
527
if (joystick->driver != &SDL_LINUX_JoystickDriver) {
528
return false;
529
}
530
/* We are assuming Linux is using evdev which should trump the old
531
* joystick methods. */
532
if (SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) {
533
return true;
534
}
535
#endif
536
return false;
537
}
538
539
/*
540
* Opens a SDL_Haptic from a SDL_Joystick.
541
*/
542
bool SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
543
{
544
#ifdef SDL_JOYSTICK_LINUX
545
int fd;
546
SDL_hapticlist_item *item;
547
const char *name;
548
549
SDL_AssertJoysticksLocked();
550
551
if (joystick->driver != &SDL_LINUX_JoystickDriver) {
552
return false;
553
}
554
// Find the joystick in the haptic list.
555
for (item = SDL_hapticlist; item; item = item->next) {
556
if (SDL_strcmp(item->fname, joystick->hwdata->fname) == 0) {
557
haptic->instance_id = item->instance_id;
558
break;
559
}
560
}
561
562
fd = open(joystick->hwdata->fname, O_RDWR | O_CLOEXEC, 0);
563
if (fd < 0) {
564
return SDL_SetError("Haptic: Unable to open %s: %s",
565
joystick->hwdata->fname, strerror(errno));
566
}
567
if (!SDL_SYS_HapticOpenFromFD(haptic, fd)) {
568
// Already closes on error.
569
return false;
570
}
571
572
haptic->hwdata->fname = SDL_strdup(joystick->hwdata->fname);
573
574
name = SDL_SYS_HapticNameFromFD(fd);
575
if (name) {
576
haptic->name = SDL_strdup(name);
577
}
578
return true;
579
#else
580
return false;
581
#endif
582
}
583
584
/*
585
* Closes the haptic device.
586
*/
587
void SDL_SYS_HapticClose(SDL_Haptic *haptic)
588
{
589
if (haptic->hwdata) {
590
591
// Free effects.
592
SDL_free(haptic->effects);
593
haptic->effects = NULL;
594
haptic->neffects = 0;
595
596
// Clean up
597
close(haptic->hwdata->fd);
598
599
// Free
600
SDL_free(haptic->hwdata->fname);
601
SDL_free(haptic->hwdata);
602
haptic->hwdata = NULL;
603
}
604
605
// Clear the rest.
606
SDL_memset(haptic, 0, sizeof(SDL_Haptic));
607
}
608
609
/*
610
* Clean up after system specific haptic stuff
611
*/
612
void SDL_SYS_HapticQuit(void)
613
{
614
SDL_hapticlist_item *item = NULL;
615
SDL_hapticlist_item *next = NULL;
616
617
for (item = SDL_hapticlist; item; item = next) {
618
next = item->next;
619
/* Opened and not closed haptics are leaked, this is on purpose.
620
* Close your haptic devices after usage. */
621
SDL_free(item->fname);
622
SDL_free(item);
623
}
624
625
#ifdef SDL_USE_LIBUDEV
626
SDL_UDEV_DelCallback(haptic_udev_callback);
627
SDL_UDEV_Quit();
628
#endif // SDL_USE_LIBUDEV
629
630
numhaptics = 0;
631
SDL_hapticlist = NULL;
632
SDL_hapticlist_tail = NULL;
633
}
634
635
/*
636
* Converts an SDL button to a ff_trigger button.
637
*/
638
static Uint16 SDL_SYS_ToButton(Uint16 button)
639
{
640
Uint16 ff_button;
641
642
ff_button = 0;
643
644
/*
645
* Not sure what the proper syntax is because this actually isn't implemented
646
* in the current kernel from what I've seen (2.6.26).
647
*/
648
if (button != 0) {
649
ff_button = BTN_GAMEPAD + button - 1;
650
}
651
652
return ff_button;
653
}
654
655
/*
656
* Initializes the ff_effect usable direction from a SDL_HapticDirection.
657
*/
658
static bool SDL_SYS_ToDirection(Uint16 *dest, const SDL_HapticDirection *src)
659
{
660
Uint32 tmp;
661
662
switch (src->type) {
663
case SDL_HAPTIC_POLAR:
664
tmp = ((src->dir[0] % 36000) * 0x8000) / 18000; // convert to range [0,0xFFFF]
665
*dest = (Uint16)tmp;
666
break;
667
668
case SDL_HAPTIC_SPHERICAL:
669
/*
670
We convert to polar, because that's the only supported direction on Linux.
671
The first value of a spherical direction is practically the same as a
672
Polar direction, except that we have to add 90 degrees. It is the angle
673
from EAST {1,0} towards SOUTH {0,1}.
674
--> add 9000
675
--> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
676
*/
677
tmp = ((src->dir[0]) + 9000) % 36000; // Convert to polars
678
tmp = (tmp * 0x8000) / 18000; // convert to range [0,0xFFFF]
679
*dest = (Uint16)tmp;
680
break;
681
682
case SDL_HAPTIC_CARTESIAN:
683
if (!src->dir[1]) {
684
*dest = (src->dir[0] >= 0 ? 0x4000 : 0xC000);
685
} else if (!src->dir[0]) {
686
*dest = (src->dir[1] >= 0 ? 0x8000 : 0);
687
} else {
688
float f = SDL_atan2f(src->dir[1], src->dir[0]); // Ideally we'd use fixed point math instead of floats...
689
/*
690
SDL_atan2 takes the parameters: Y-axis-value and X-axis-value (in that order)
691
- Y-axis-value is the second coordinate (from center to SOUTH)
692
- X-axis-value is the first coordinate (from center to EAST)
693
We add 36000, because SDL_atan2 also returns negative values. Then we practically
694
have the first spherical value. Therefore we proceed as in case
695
SDL_HAPTIC_SPHERICAL and add another 9000 to get the polar value.
696
--> add 45000 in total
697
--> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
698
*/
699
tmp = (((Sint32)(f * 18000.0 / SDL_PI_D)) + 45000) % 36000;
700
tmp = (tmp * 0x8000) / 18000; // convert to range [0,0xFFFF]
701
*dest = (Uint16)tmp;
702
}
703
break;
704
case SDL_HAPTIC_STEERING_AXIS:
705
*dest = 0x4000;
706
break;
707
default:
708
return SDL_SetError("Haptic: Unsupported direction type.");
709
}
710
711
return true;
712
}
713
714
#define CLAMP(x) (((x) > 32767) ? 32767 : x)
715
/*
716
* Initializes the Linux effect struct from a haptic_effect.
717
* Values above 32767 (for unsigned) are unspecified so we must clamp.
718
*/
719
static bool SDL_SYS_ToFFEffect(struct ff_effect *dest, const SDL_HapticEffect *src)
720
{
721
const SDL_HapticConstant *constant;
722
const SDL_HapticPeriodic *periodic;
723
const SDL_HapticCondition *condition;
724
const SDL_HapticRamp *ramp;
725
const SDL_HapticLeftRight *leftright;
726
727
// Clear up
728
SDL_memset(dest, 0, sizeof(struct ff_effect));
729
730
switch (src->type) {
731
case SDL_HAPTIC_CONSTANT:
732
constant = &src->constant;
733
734
// Header
735
dest->type = FF_CONSTANT;
736
if (!SDL_SYS_ToDirection(&dest->direction, &constant->direction)) {
737
return false;
738
}
739
740
// Replay
741
dest->replay.length = (constant->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(constant->length);
742
dest->replay.delay = CLAMP(constant->delay);
743
744
// Trigger
745
dest->trigger.button = SDL_SYS_ToButton(constant->button);
746
dest->trigger.interval = CLAMP(constant->interval);
747
748
// Constant
749
dest->u.constant.level = constant->level;
750
751
// Envelope
752
dest->u.constant.envelope.attack_length =
753
CLAMP(constant->attack_length);
754
dest->u.constant.envelope.attack_level =
755
CLAMP(constant->attack_level);
756
dest->u.constant.envelope.fade_length = CLAMP(constant->fade_length);
757
dest->u.constant.envelope.fade_level = CLAMP(constant->fade_level);
758
759
break;
760
761
case SDL_HAPTIC_SINE:
762
case SDL_HAPTIC_SQUARE:
763
case SDL_HAPTIC_TRIANGLE:
764
case SDL_HAPTIC_SAWTOOTHUP:
765
case SDL_HAPTIC_SAWTOOTHDOWN:
766
periodic = &src->periodic;
767
768
// Header
769
dest->type = FF_PERIODIC;
770
if (!SDL_SYS_ToDirection(&dest->direction, &periodic->direction)) {
771
return false;
772
}
773
774
// Replay
775
dest->replay.length = (periodic->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(periodic->length);
776
dest->replay.delay = CLAMP(periodic->delay);
777
778
// Trigger
779
dest->trigger.button = SDL_SYS_ToButton(periodic->button);
780
dest->trigger.interval = CLAMP(periodic->interval);
781
782
// Periodic
783
if (periodic->type == SDL_HAPTIC_SINE) {
784
dest->u.periodic.waveform = FF_SINE;
785
} else if (periodic->type == SDL_HAPTIC_SQUARE) {
786
dest->u.periodic.waveform = FF_SQUARE;
787
} else if (periodic->type == SDL_HAPTIC_TRIANGLE) {
788
dest->u.periodic.waveform = FF_TRIANGLE;
789
} else if (periodic->type == SDL_HAPTIC_SAWTOOTHUP) {
790
dest->u.periodic.waveform = FF_SAW_UP;
791
} else if (periodic->type == SDL_HAPTIC_SAWTOOTHDOWN) {
792
dest->u.periodic.waveform = FF_SAW_DOWN;
793
}
794
dest->u.periodic.period = CLAMP(periodic->period);
795
dest->u.periodic.magnitude = periodic->magnitude;
796
dest->u.periodic.offset = periodic->offset;
797
// Linux phase is defined in interval "[0x0000, 0x10000[", corresponds with "[0deg, 360deg[" phase shift.
798
dest->u.periodic.phase = ((Uint32)periodic->phase * 0x10000U) / 36000;
799
800
// Envelope
801
dest->u.periodic.envelope.attack_length =
802
CLAMP(periodic->attack_length);
803
dest->u.periodic.envelope.attack_level =
804
CLAMP(periodic->attack_level);
805
dest->u.periodic.envelope.fade_length = CLAMP(periodic->fade_length);
806
dest->u.periodic.envelope.fade_level = CLAMP(periodic->fade_level);
807
808
break;
809
810
case SDL_HAPTIC_SPRING:
811
case SDL_HAPTIC_DAMPER:
812
case SDL_HAPTIC_INERTIA:
813
case SDL_HAPTIC_FRICTION:
814
condition = &src->condition;
815
816
// Header
817
if (condition->type == SDL_HAPTIC_SPRING) {
818
dest->type = FF_SPRING;
819
} else if (condition->type == SDL_HAPTIC_DAMPER) {
820
dest->type = FF_DAMPER;
821
} else if (condition->type == SDL_HAPTIC_INERTIA) {
822
dest->type = FF_INERTIA;
823
} else if (condition->type == SDL_HAPTIC_FRICTION) {
824
dest->type = FF_FRICTION;
825
}
826
827
if (!SDL_SYS_ToDirection(&dest->direction, &condition->direction)) {
828
return false;
829
}
830
831
// Replay
832
dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(condition->length);
833
dest->replay.delay = CLAMP(condition->delay);
834
835
// Trigger
836
dest->trigger.button = SDL_SYS_ToButton(condition->button);
837
dest->trigger.interval = CLAMP(condition->interval);
838
839
// Condition
840
// X axis
841
dest->u.condition[0].right_saturation = condition->right_sat[0];
842
dest->u.condition[0].left_saturation = condition->left_sat[0];
843
dest->u.condition[0].right_coeff = condition->right_coeff[0];
844
dest->u.condition[0].left_coeff = condition->left_coeff[0];
845
dest->u.condition[0].deadband = condition->deadband[0];
846
dest->u.condition[0].center = condition->center[0];
847
// Y axis
848
dest->u.condition[1].right_saturation = condition->right_sat[1];
849
dest->u.condition[1].left_saturation = condition->left_sat[1];
850
dest->u.condition[1].right_coeff = condition->right_coeff[1];
851
dest->u.condition[1].left_coeff = condition->left_coeff[1];
852
dest->u.condition[1].deadband = condition->deadband[1];
853
dest->u.condition[1].center = condition->center[1];
854
855
/*
856
* There is no envelope in the linux force feedback api for conditions.
857
*/
858
859
break;
860
861
case SDL_HAPTIC_RAMP:
862
ramp = &src->ramp;
863
864
// Header
865
dest->type = FF_RAMP;
866
if (!SDL_SYS_ToDirection(&dest->direction, &ramp->direction)) {
867
return false;
868
}
869
870
// Replay
871
dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(ramp->length);
872
dest->replay.delay = CLAMP(ramp->delay);
873
874
// Trigger
875
dest->trigger.button = SDL_SYS_ToButton(ramp->button);
876
dest->trigger.interval = CLAMP(ramp->interval);
877
878
// Ramp
879
dest->u.ramp.start_level = ramp->start;
880
dest->u.ramp.end_level = ramp->end;
881
882
// Envelope
883
dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length);
884
dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level);
885
dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length);
886
dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level);
887
888
break;
889
890
case SDL_HAPTIC_LEFTRIGHT:
891
leftright = &src->leftright;
892
893
// Header
894
dest->type = FF_RUMBLE;
895
dest->direction = 0x4000;
896
897
// Replay
898
dest->replay.length = (leftright->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(leftright->length);
899
900
// Trigger
901
dest->trigger.button = 0;
902
dest->trigger.interval = 0;
903
904
// Rumble (Linux expects 0-65535, so multiply by 2)
905
dest->u.rumble.strong_magnitude = CLAMP(leftright->large_magnitude) * 2;
906
dest->u.rumble.weak_magnitude = CLAMP(leftright->small_magnitude) * 2;
907
908
break;
909
910
default:
911
return SDL_SetError("Haptic: Unknown effect type.");
912
}
913
914
return true;
915
}
916
917
/*
918
* Creates a new haptic effect.
919
*/
920
bool SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
921
const SDL_HapticEffect *base)
922
{
923
struct ff_effect *linux_effect;
924
925
// Allocate the hardware effect
926
effect->hweffect = (struct haptic_hweffect *)
927
SDL_calloc(1, sizeof(struct haptic_hweffect));
928
if (!effect->hweffect) {
929
return false;
930
}
931
932
// Prepare the ff_effect
933
linux_effect = &effect->hweffect->effect;
934
if (!SDL_SYS_ToFFEffect(linux_effect, base)) {
935
goto new_effect_err;
936
}
937
linux_effect->id = -1; // Have the kernel give it an id
938
939
// Upload the effect
940
if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
941
SDL_SetError("Haptic: Error uploading effect to the device: %s",
942
strerror(errno));
943
goto new_effect_err;
944
}
945
946
return true;
947
948
new_effect_err:
949
SDL_free(effect->hweffect);
950
effect->hweffect = NULL;
951
return false;
952
}
953
954
/*
955
* Updates an effect.
956
*
957
* Note: Dynamically updating the direction can in some cases force
958
* the effect to restart and run once.
959
*/
960
bool SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic,
961
struct haptic_effect *effect,
962
const SDL_HapticEffect *data)
963
{
964
struct ff_effect linux_effect;
965
966
// Create the new effect
967
if (!SDL_SYS_ToFFEffect(&linux_effect, data)) {
968
return false;
969
}
970
linux_effect.id = effect->hweffect->effect.id;
971
972
// See if it can be uploaded.
973
if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
974
return SDL_SetError("Haptic: Error updating the effect: %s",
975
strerror(errno));
976
}
977
978
// Copy the new effect into memory.
979
SDL_memcpy(&effect->hweffect->effect, &linux_effect,
980
sizeof(struct ff_effect));
981
982
return true;
983
}
984
985
/*
986
* Runs an effect.
987
*/
988
bool SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
989
Uint32 iterations)
990
{
991
struct input_event run;
992
993
// Prepare to run the effect
994
run.type = EV_FF;
995
run.code = effect->hweffect->effect.id;
996
// We don't actually have infinity here, so we just do INT_MAX which is pretty damn close.
997
run.value = (iterations > INT_MAX) ? INT_MAX : iterations;
998
999
if (write(haptic->hwdata->fd, (const void *)&run, sizeof(run)) < 0) {
1000
return SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno));
1001
}
1002
1003
return true;
1004
}
1005
1006
/*
1007
* Stops an effect.
1008
*/
1009
bool SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
1010
{
1011
struct input_event stop;
1012
1013
stop.type = EV_FF;
1014
stop.code = effect->hweffect->effect.id;
1015
stop.value = 0;
1016
1017
if (write(haptic->hwdata->fd, (const void *)&stop, sizeof(stop)) < 0) {
1018
return SDL_SetError("Haptic: Unable to stop the effect: %s",
1019
strerror(errno));
1020
}
1021
1022
return true;
1023
}
1024
1025
/*
1026
* Frees the effect.
1027
*/
1028
void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
1029
{
1030
if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) {
1031
SDL_SetError("Haptic: Error removing the effect from the device: %s",
1032
strerror(errno));
1033
}
1034
SDL_free(effect->hweffect);
1035
effect->hweffect = NULL;
1036
}
1037
1038
/*
1039
* Gets the status of a haptic effect.
1040
*/
1041
int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic,
1042
struct haptic_effect *effect)
1043
{
1044
#if 0 // Not supported atm.
1045
struct input_event ie;
1046
1047
ie.type = EV_FF;
1048
ie.type = EV_FF_STATUS;
1049
ie.code = effect->hweffect->effect.id;
1050
1051
if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1052
SDL_SetError("Haptic: Error getting device status.");
1053
return -1;
1054
}
1055
1056
return 1;
1057
#endif
1058
1059
SDL_Unsupported();
1060
return -1;
1061
}
1062
1063
/*
1064
* Sets the gain.
1065
*/
1066
bool SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
1067
{
1068
struct input_event ie;
1069
1070
ie.type = EV_FF;
1071
ie.code = FF_GAIN;
1072
ie.value = (0xFFFFUL * gain) / 100;
1073
1074
if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1075
return SDL_SetError("Haptic: Error setting gain: %s", strerror(errno));
1076
}
1077
1078
return true;
1079
}
1080
1081
/*
1082
* Sets the autocentering.
1083
*/
1084
bool SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
1085
{
1086
struct input_event ie;
1087
1088
ie.type = EV_FF;
1089
ie.code = FF_AUTOCENTER;
1090
ie.value = (0xFFFFUL * autocenter) / 100;
1091
1092
if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
1093
return SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno));
1094
}
1095
1096
return true;
1097
}
1098
1099
/*
1100
* Pausing is not supported atm by linux.
1101
*/
1102
bool SDL_SYS_HapticPause(SDL_Haptic *haptic)
1103
{
1104
return SDL_Unsupported();
1105
}
1106
1107
/*
1108
* Unpausing is not supported atm by linux.
1109
*/
1110
bool SDL_SYS_HapticResume(SDL_Haptic *haptic)
1111
{
1112
return SDL_Unsupported();
1113
}
1114
1115
/*
1116
* Stops all the currently playing effects.
1117
*/
1118
bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
1119
{
1120
int i, ret;
1121
1122
// Linux does not support this natively so we have to loop.
1123
for (i = 0; i < haptic->neffects; i++) {
1124
if (haptic->effects[i].hweffect != NULL) {
1125
ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]);
1126
if (ret < 0) {
1127
return SDL_SetError("Haptic: Error while trying to stop all playing effects.");
1128
}
1129
}
1130
}
1131
return true;
1132
}
1133
1134
#endif // SDL_HAPTIC_LINUX
1135
1136