aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2022-07-25 12:35:05 +0200
committerMattias Andrée <maandree@kth.se>2022-07-25 12:35:05 +0200
commit41ae3f04fd22d22c552742458344591061822884 (patch)
tree00ef785370327ebf578efa49a38c430523c4924a
parentAdd test-visual (diff)
downloadlibgamepad-41ae3f04fd22d22c552742458344591061822884.tar.gz
libgamepad-41ae3f04fd22d22c552742458344591061822884.tar.bz2
libgamepad-41ae3f04fd22d22c552742458344591061822884.tar.xz
m + add force feedback support
Signed-off-by: Mattias Andrée <maandree@kth.se>
-rw-r--r--Makefile21
-rw-r--r--TODO4
-rw-r--r--common.h1
-rw-r--r--config.mk2
-rw-r--r--libgamepad.h542
-rw-r--r--libgamepad_construct_constant_force_feedback_effect.c7
-rw-r--r--libgamepad_construct_damper_force_feedback_effect.c7
-rw-r--r--libgamepad_construct_force_feedback_effect__.c24
-rw-r--r--libgamepad_construct_friction_force_feedback_effect.c7
-rw-r--r--libgamepad_construct_inertia_force_feedback_effect.c7
-rw-r--r--libgamepad_construct_periodic_force_feedback_effect.c7
-rw-r--r--libgamepad_construct_ramp_force_feedback_effect.c7
-rw-r--r--libgamepad_construct_rumble_force_feedback_effect.c7
-rw-r--r--libgamepad_construct_spring_force_feedback_effect.c7
-rw-r--r--libgamepad_disable_force_feedback_autocenter.c5
-rw-r--r--libgamepad_get_absolute_axis_info.c2
-rw-r--r--libgamepad_get_absolute_axis_name.c2
-rw-r--r--libgamepad_get_button_is_pressed.c2
-rw-r--r--libgamepad_get_button_name.c2
-rw-r--r--libgamepad_get_force_feedback_max_concurrency.c5
-rw-r--r--libgamepad_get_relative_axis_name.c2
-rw-r--r--libgamepad_install_force_feedback_effect.c5
-rw-r--r--libgamepad_is_force_feedback_effect_supported.c5
-rw-r--r--libgamepad_open_device.c5
-rw-r--r--libgamepad_play_force_feedback_effect.c5
-rw-r--r--libgamepad_set_force_feedback_autocenter.c5
-rw-r--r--libgamepad_set_force_feedback_master_gain.c5
-rw-r--r--libgamepad_stop_force_feedback_effect.c5
-rw-r--r--libgamepad_uninstall_force_feedback_effect.c5
-rw-r--r--libgamepad_update_force_feedback_effect.c5
-rw-r--r--test-details.c2
-rw-r--r--test-visual.c15
32 files changed, 671 insertions, 61 deletions
diff --git a/Makefile b/Makefile
index 67bc8d5..36e7dcc 100644
--- a/Makefile
+++ b/Makefile
@@ -19,21 +19,40 @@ LIB_NAME = gamepad
OBJ =\
libgamepad_close_device.o\
libgamepad_close_superdevice.o\
+ libgamepad_construct_constant_force_feedback_effect.o\
+ libgamepad_construct_damper_force_feedback_effect.o\
+ libgamepad_construct_force_feedback_effect__.o\
+ libgamepad_construct_friction_force_feedback_effect.o\
+ libgamepad_construct_inertia_force_feedback_effect.o\
+ libgamepad_construct_periodic_force_feedback_effect.o\
+ libgamepad_construct_ramp_force_feedback_effect.o\
+ libgamepad_construct_rumble_force_feedback_effect.o\
+ libgamepad_construct_spring_force_feedback_effect.o\
libgamepad_create_attachment_monitor.o\
libgamepad_destroy_attachment_monitor.o\
+ libgamepad_disable_force_feedback_autocenter.o\
libgamepad_get_absolute_axis_info.o\
libgamepad_get_absolute_axis_name.o\
libgamepad_get_attachment_event.o\
libgamepad_get_button_is_pressed.o\
libgamepad_get_button_name.o\
+ libgamepad_get_force_feedback_max_concurrency.o\
libgamepad_get_relative_axis_name.o\
libgamepad_grab.o\
+ libgamepad_install_force_feedback_effect.o\
+ libgamepad_is_force_feedback_effect_supported.o\
libgamepad_list_superdevices.o\
libgamepad_next_event.o\
libgamepad_open_device.o\
libgamepad_open_superdevice.o\
+ libgamepad_play_force_feedback_effect.o\
libgamepad_set_clock.o\
- libgamepad_ungrab.o
+ libgamepad_set_force_feedback_autocenter.o\
+ libgamepad_set_force_feedback_master_gain.o\
+ libgamepad_stop_force_feedback_effect.o\
+ libgamepad_ungrab.o\
+ libgamepad_uninstall_force_feedback_effect.o\
+ libgamepad_update_force_feedback_effect.o
HDR =\
common.h\
diff --git a/TODO b/TODO
index dc7dd9d..c8bccdc 100644
--- a/TODO
+++ b/TODO
@@ -1,7 +1,9 @@
Add support for quirks
Add support for battery
Add support for leds
-Add support for force feedback
Add support for audio
+Add support for accelerometer
+Add support for creating instance from a file descriptor
Add man pages
Add readme file
+Add force feedback tests
diff --git a/common.h b/common.h
index 09f6193..88acdf0 100644
--- a/common.h
+++ b/common.h
@@ -6,6 +6,7 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <math.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
diff --git a/config.mk b/config.mk
index 77acc18..5c13103 100644
--- a/config.mk
+++ b/config.mk
@@ -8,4 +8,4 @@ CFLAGS = -Wall -g
LDFLAGS =
LIBS_CFLAGS = $$(pkg-config --cflags libevdev)
-LIBS_LDFLAGS = $$(pkg-config --libs libevdev) -ludev
+LIBS_LDFLAGS = $$(pkg-config --libs libevdev) -ludev -lm
diff --git a/libgamepad.h b/libgamepad.h
index 627589a..665e012 100644
--- a/libgamepad.h
+++ b/libgamepad.h
@@ -2,11 +2,15 @@
#ifndef LIBGAMEPAD_H
#define LIBGAMEPAD_H
+#include <linux/input.h>
+#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stddef.h>
+#include <string.h>
#include <time.h>
+#include <unistd.h>
#include <libevdev/libevdev.h>
@@ -255,6 +259,21 @@ struct libgamepad_device {
size_t nrelative_axes;
/**
+ * Map from button/key indices to button/key codes
+ */
+ uint16_t *buttons;
+
+ /**
+ * Map from absolute axis indices to absolute axis codes
+ */
+ uint16_t *absolute_axes;
+
+ /**
+ * Map from relative axis indices to relative axis codes
+ */
+ uint16_t *relative_axes;
+
+ /**
* Maps from button/key codes to button indices,
* non-present buttons/keys map to -1, other
* values are in [0, `.nbuttons`[.
@@ -276,20 +295,14 @@ struct libgamepad_device {
int16_t relative_axis_map[REL_CNT];
/**
- * Map from button/key indices to button/key codes
+ * Bitmap of supported force feedback effects
*/
- uint16_t *buttons;
+ uint8_t force_feedback_support[(FF_CNT + 7) / 8];
+};
- /**
- * Map from absolute axis indices to absolute axis codes
- */
- uint16_t *absolute_axes;
- /**
- * Map from relative axis indices to relative axis codes
- */
- uint16_t *relative_axes;
-};
+/* FOR INTERNAL USE */
+void libgamepad_construct_force_feedback_effect__(struct ff_effect *, const struct ff_effect *, double, uint16_t, uint16_t);
/**
@@ -422,7 +435,7 @@ void libgamepad_close_device(struct libgamepad_device *);
* libevdev_next_event(3), including:
* - EAGAIN no more data currently available (non-blocking mode)
* - EINTR system call was interrupted, try again
- * - ENODEV device has been unplugged
+ * - ENODEV device has been unplugged or access has been revoked
*/
int libgamepad_next_event(struct libgamepad_device *, struct libgamepad_input_event *);
@@ -434,9 +447,9 @@ int libgamepad_next_event(struct libgamepad_device *, struct libgamepad_input_ev
* @return The button/key's name
*/
inline const char *
-libgamepad_get_button_name(unsigned int code)
+libgamepad_get_button_name(uint16_t code)
{
- return libevdev_event_code_get_name(EV_KEY, code);
+ return libevdev_event_code_get_name(EV_KEY, (unsigned int)code);
}
/**
@@ -446,9 +459,9 @@ libgamepad_get_button_name(unsigned int code)
* @return The axis' name
*/
inline const char *
-libgamepad_get_absolute_axis_name(unsigned int code)
+libgamepad_get_absolute_axis_name(uint16_t code)
{
- return libevdev_event_code_get_name(EV_ABS, code);
+ return libevdev_event_code_get_name(EV_ABS, (unsigned int)code);
}
/**
@@ -458,9 +471,9 @@ libgamepad_get_absolute_axis_name(unsigned int code)
* @return The axis' name
*/
inline const char *
-libgamepad_get_relative_axis_name(unsigned int code)
+libgamepad_get_relative_axis_name(uint16_t code)
{
- return libevdev_event_code_get_name(EV_REL, code);
+ return libevdev_event_code_get_name(EV_REL, (unsigned int)code);
}
/**
@@ -472,9 +485,9 @@ libgamepad_get_relative_axis_name(unsigned int code)
* 0 otherwise
*/
inline int
-libgamepad_get_button_is_pressed(struct libgamepad_device *device, unsigned int code)
+libgamepad_get_button_is_pressed(struct libgamepad_device *device, uint16_t code)
{
- return libevdev_get_event_value(device->dev, EV_KEY, code);
+ return libevdev_get_event_value(device->dev, EV_KEY, (unsigned int)code);
}
/**
@@ -485,9 +498,9 @@ libgamepad_get_button_is_pressed(struct libgamepad_device *device, unsigned int
* @return Information about the axis
*/
inline const struct input_absinfo * /* `struct input_absinfo` is defined in <linux/input.h> */
-libgamepad_get_absolute_axis_info(struct libgamepad_device *device, unsigned int code)
+libgamepad_get_absolute_axis_info(struct libgamepad_device *device, uint16_t code)
{
- return libevdev_get_abs_info(device->dev, code);
+ return libevdev_get_abs_info(device->dev, (unsigned int)code);
}
@@ -502,23 +515,15 @@ libgamepad_get_absolute_axis_info(struct libgamepad_device *device, unsigned int
* @param device The device to grab
* @return 0 on success, -1 on failure
*
- * May fail for any reason specified for libevdev_grab(3)
- * with LIBEVDEV_GRAB applied
- *
* In the event that the device is already grabbed by
- * via another file descriptor (duplicates do not share
+ * via another open file descriptor (duplicates share
* grab), this function will return -1 and set `errno`
* to `EBUSY`
*/
inline int
libgamepad_grab(struct libgamepad_device *device)
{
- int err = libevdev_grab(device->dev, LIBEVDEV_GRAB);
- if (err < 0) {
- errno = -err;
- return -1;
- }
- return 0;
+ return ioctl(device->fd, EVIOCGRAB, (void *)1);
}
/**
@@ -526,19 +531,11 @@ libgamepad_grab(struct libgamepad_device *device)
*
* @param device The device to ungrab
* @return 0 on success, -1 on failure
- *
- * May fail for any reason specified for libevdev_grab(3)
- * with LIBEVDEV_UNGRAB applied
*/
inline int
libgamepad_ungrab(struct libgamepad_device *device)
{
- int err = libevdev_grab(device->dev, LIBEVDEV_UNGRAB);
- if (err < 0) {
- errno = -err;
- return -1;
- }
- return 0;
+ return ioctl(device->fd, EVIOCGRAB, (void *)0);
}
@@ -548,18 +545,465 @@ libgamepad_ungrab(struct libgamepad_device *device)
* @param device The device to configure
* @param clockid The clock to use on event timestamps
* @return 0 on success, -1 on failure
- *
- * May fail for any reason specified for libevdev_set_clock_id(3)
*/
inline int
libgamepad_set_clock(struct libgamepad_device *device, clockid_t clockid)
{
- int err = libevdev_set_clock_id(device->dev, clockid);
- if (err < 0) {
- errno = -err;
- return -1;
- }
- return 0;
+ return ioctl(device->fd, EVIOCSCLOCKID, &clockid);
+}
+
+
+/**
+ * Install a force feedback effect that can be played back later
+ *
+ * @param device The device to configure
+ * @param effect The effect to install; will be edited, specifically the ID
+ * will be set (need not be set before calling this function)
+ * @return 0 on success, -1 on failure
+ */
+inline int /* `struct ff_effect` is defined in <linux/input.h> */
+libgamepad_install_force_feedback_effect(struct libgamepad_device *device, struct ff_effect *effect)
+{
+ effect->id = -1;
+ return ioctl(device->fd, EVIOCSFF, effect);
+}
+
+/**
+ * Reconfigure an already installed force feedback effect
+ *
+ * @param device The device to configure
+ * @param effect The effect to update, the ID must be set to the ID of a
+ * previously installed effect
+ * @return 0 on success, -1 on failure
+ */
+inline int
+libgamepad_update_force_feedback_effect(struct libgamepad_device *device, struct ff_effect *effect)
+{
+ return ioctl(device->fd, EVIOCSFF, effect);
+}
+
+/**
+ * Uninstall an installed force feedback effect
+ *
+ * @param device The device to configure
+ * @param effect The effect to remove, the ID must be set to the ID of a
+ * previously installed effect and will be reset
+ * @return 0 on success, -1 on failure
+ */
+inline int
+libgamepad_uninstall_force_feedback_effect(struct libgamepad_device *device, struct ff_effect *effect)
+{
+ int ret = ioctl(device->fd, EVIOCRMFF, effect);
+ effect->id = -1;
+ return ret;
+}
+
+/**
+ * Get the number of force feedback effects that can be played concurrently
+ *
+ * @param device The device to configure
+ * @return The number of maximum number concurrent effects, -1 on failure
+ */
+inline int
+libgamepad_get_force_feedback_max_concurrency(struct libgamepad_device *device)
+{
+ int num;
+ return ioctl(device->fd, EVIOCGEFFECTS, &num) ? -1 : num;
+}
+
+/**
+ * Play a force feedback effect
+ *
+ * @param device The device to play the force feedback effect on
+ * @param effect The force feedback effect to play, must already be installed
+ * with `libgamepad_install_force_feedback_effect`
+ * @return 0 on success, -1 on failure
+ */
+inline int
+libgamepad_play_force_feedback_effect(struct libgamepad_device *device, const struct ff_effect *effect)
+{
+ struct input_event message;
+ message.type = EV_FF;
+ message.code = (uint16_t)effect->id;
+ message.value = 1;
+ return write(device->fd, &message, sizeof(message)) < 0 ? -1 : 0;
+}
+
+/**
+ * Stop playing a force feedback effect
+ *
+ * @param device The device playing the force feedback effect on
+ * @param effect The force feedback effect to play, must already be installed
+ * with `libgamepad_install_force_feedback_effect`
+ * @return 0 on success, -1 on failure
+ */
+inline int
+libgamepad_stop_force_feedback_effect(struct libgamepad_device *device, const struct ff_effect *effect)
+{
+ struct input_event message;
+ message.type = EV_FF;
+ message.code = (uint16_t)effect->id;
+ message.value = 0;
+ return write(device->fd, &message, sizeof(message)) < 0 ? -1 : 0;
+}
+
+/**
+ * Check if a force feedback effect or waveform is supported
+ *
+ * @param device The device to check force feedback support on
+ * @param effect The force feedback effect or waveform
+ * @return 1 if the effect/waveform is supported, 0 otherwise
+ */
+inline int
+libgamepad_is_force_feedback_effect_supported(struct libgamepad_device *device, uint16_t effect)
+{
+ return effect < sizeof(device->force_feedback_support) * 8 &&
+ ((device->force_feedback_support[effect / 8] >> (effect % 8)) & 1);
+}
+
+/**
+ * Set the force feedback master gain on a device
+ *
+ * Requires support for `FF_GAIN` (check with
+ * `libgamepad_is_force_feedback_effect_supported`)
+ *
+ * @param device The device to configure
+ * @param gain The master gain, shall be a value in [0, 0xFFFF]
+ * @return 0 on success, -1 on failure
+ */
+inline int
+libgamepad_set_force_feedback_master_gain(struct libgamepad_device *device, uint16_t gain)
+{
+ struct input_event message;
+ message.type = EV_FF;
+ message.code = FF_GAIN;
+ message.value = (int16_t)gain;
+ return write(device->fd, &message, sizeof(message)) < 0 ? -1 : 0;
+}
+
+/**
+ * Set the autocenter force feedback on a device
+ *
+ * Requires support for `FF_AUTOCENTER` (check with
+ * `libgamepad_is_force_feedback_effect_supported`)
+ *
+ * @param device The device to configure
+ * @param autocenter Autocenter strength, shall be a value in [0, 0xFFFF], select 0 to
+ * disable (you can also use `libgamepad_disable_force_feedback_autocenter`)
+ * @return 0 on success, -1 on failure
+ */
+inline int
+libgamepad_set_force_feedback_autocenter(struct libgamepad_device *device, uint16_t autocenter)
+{
+ struct input_event message;
+ message.type = EV_FF;
+ message.code = FF_AUTOCENTER;
+ message.value = (int16_t)autocenter;
+ return write(device->fd, &message, sizeof(message)) < 0 ? -1 : 0;
+}
+
+/**
+ * Disable the autocenter force feedback on a device
+ *
+ * Requires support for `FF_AUTOCENTER` (check with
+ * `libgamepad_is_force_feedback_effect_supported`)
+ *
+ * @param device device to configure
+ * @return 0 on success, -1 on failure
+ */
+inline int
+libgamepad_disable_force_feedback_autocenter(struct libgamepad_device *device)
+{
+ struct input_event message;
+ message.type = EV_FF;
+ message.code = FF_AUTOCENTER;
+ message.value = 0;
+ return write(device->fd, &message, sizeof(message)) < 0 ? -1 : 0;
+}
+
+/**
+ * Construct a rumble force-feedback effect
+ *
+ * Requires support for `FF_RUMBLE` (check with
+ * `libgamepad_is_force_feedback_effect_supported`)
+ *
+ * This function will set the following values to
+ * zero if `from` is `NULL`, see <linux/input.h>
+ * for more details:
+ * - effectp->trigger.button
+ * - effectp->trigger.interval
+ * - effectp->replay.delay
+ *
+ * @param effectp Output parameter for the effect
+ * @param from Effect to reconfigure, `NULL` to create a new effect
+ * @param direction The direction of the force feedback (if directional), where
+ * values are measure clockwise and both 0 and 1 are upwards
+ * @param length The number of milliseconds the effect is played
+ * @param effect Effect specific details:
+ * - .strong_magnitude Heavy motor strength, unsigned 16-bit integer
+ * - .weak_magnitude Light motor strength, unsigned 16-bit integer
+ */
+inline void
+libgamepad_construct_rumble_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
+ double direction, uint16_t length, const struct ff_rumble_effect *effect)
+{
+ libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_RUMBLE);
+ memcpy(&effectp->u.rumble, effect, sizeof(effectp->u.rumble));
+}
+
+/**
+ * Construct a constant force-feedback effect
+ *
+ * Requires support for `FF_CONSTANT` (check with
+ * `libgamepad_is_force_feedback_effect_supported`)
+ *
+ * This function will set the following values to
+ * zero if `from` is `NULL`, see <linux/input.h>
+ * for more details:
+ * - effectp->trigger.button
+ * - effectp->trigger.interval
+ * - effectp->replay.delay
+ *
+ * @param effectp Output parameter for the effect
+ * @param from Effect to reconfigure, `NULL` to create a new effect
+ * @param direction The direction of the force feedback (if directional), where
+ * values are measure clockwise and both 0 and 1 are upwards
+ * @param length The number of milliseconds the effect is played
+ * @param effect Effect specific details:
+ * - .level Strength, signed 16-bit integer
+ * - .envelope.attack_strength Strength at beginning, unsigned 16-bit integer
+ * - .envelope.fade_strength Strength at end, unsigned 16-bit integer
+ * - .envelope.attack_length The number of milliseconds the strength shall
+ * transition from `.envelope.attack_strength`
+ * to `.level`, unsigned 16-bit integer
+ * - .envelope.fade_length The number of milliseconds the strength shall
+ * transition to `.envelope.fade_strength`
+ * from `.level`, unsigned 16-bit integer
+ */
+inline void
+libgamepad_construct_constant_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
+ double direction, uint16_t length, const struct ff_constant_effect *effect)
+{
+ libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_CONSTANT);
+ memcpy(&effectp->u.constant, effect, sizeof(effectp->u.constant));
+}
+
+/**
+ * Construct a ramp force-feedback effect
+ *
+ * Requires support for `FF_RAMP` (check with
+ * `libgamepad_is_force_feedback_effect_supported`)
+ *
+ * This function will set the following values to
+ * zero if `from` is `NULL`, see <linux/input.h>
+ * for more details:
+ * - effectp->trigger.button
+ * - effectp->trigger.interval
+ * - effectp->replay.delay
+ *
+ * @param effectp Output parameter for the effect
+ * @param from Effect to reconfigure, `NULL` to create a new effect
+ * @param direction The direction of the force feedback (if directional), where
+ * values are measure clockwise and both 0 and 1 are upwards
+ * @param length The number of milliseconds the effect is played
+ * @param effect Effect specific details:
+ * - .start_level Strength at beginning, signed 16-bit integer
+ * - .end_level Strength at end, signed 16-bit integer
+ * - .envelope.attack_strength Strength at beginning, unsigned 16-bit integer
+ * - .envelope.fade_strength Strength at end, unsigned 16-bit integer
+ * - .envelope.attack_length The number of milliseconds the strength shall
+ * transition from `.envelope.attack_strength`,
+ * unsigned 16-bit integer
+ * - .envelope.fade_length The number of milliseconds the strength shall
+ * transition to `.envelope.fade_strength`,
+ * unsigned 16-bit integer
+ */
+inline void
+libgamepad_construct_ramp_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
+ double direction, uint16_t length, const struct ff_ramp_effect *effect)
+{
+ libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_RUMBLE);
+ memcpy(&effectp->u.ramp, effect, sizeof(effectp->u.ramp));
+}
+
+/**
+ * Construct a periodic force-feedback effect
+ *
+ * Requires support for `FF_PERIODIC` and the selected waveform
+ * (check with `libgamepad_is_force_feedback_effect_supported`)
+ *
+ * This function will set the following values to
+ * zero if `from` is `NULL`, see <linux/input.h>
+ * for more details:
+ * - effectp->trigger.button
+ * - effectp->trigger.interval
+ * - effectp->replay.delay
+ *
+ * @param effectp Output parameter for the effect
+ * @param from Effect to reconfigure, `NULL` to create a new effect
+ * @param direction The direction of the force feedback (if directional), where
+ * values are measure clockwise and both 0 and 1 are upwards
+ * @param length The number of milliseconds the effect is played
+ * @param effect Effect specific details:
+ * - .waveform `FF_SQUARE`, `FF_TRIANGLE`, `FF_SINE`, `FF_SAW_UP`,
+ * `FF_SAW_DOWN`, or `FF_CUSTOM`
+ * - .period The period of the wave, in milliseconds (unsigned 16-bit integer)
+ * - .magnitude The peak value, signed 16-bit integer
+ * - .offset The rough mean value, signed 16-bit integer
+ * - .phase Horiztonal wave-shift, in milliseconds (unsigned 16-bit integer)
+ * - .envelope.attack_strength Strength at beginning, unsigned 16-bit integer
+ * - .envelope.fade_strength Strength at end, unsigned 16-bit integer
+ * - .envelope.attack_length The number of milliseconds the strength shall
+ * transition from `.envelope.attack_strength`,
+ * unsigned 16-bit integer
+ * - .envelope.fade_length The number of milliseconds the strength shall
+ * transition to `.envelope.fade_strength`,
+ * unsigned 16-bit integer
+ * - .custom_len Set to zero unless you are using (`FF_CUSTOM`)
+ * - .custom_data Set to `NULL` unless you are using (`FF_CUSTOM`)
+ */
+inline void
+libgamepad_construct_periodic_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
+ double direction, uint16_t length, const struct ff_periodic_effect *effect)
+{
+ libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_PERIODIC);
+ memcpy(&effectp->u.periodic, effect, sizeof(effectp->u.periodic));
+}
+
+/**
+ * Construct a spring force-feedback effect
+ *
+ * Requires support for `FF_SPRING` (check with
+ * `libgamepad_is_force_feedback_effect_supported`)
+ *
+ * This function will set the following values to
+ * zero if `from` is `NULL`, see <linux/input.h>
+ * for more details:
+ * - effectp->trigger.button
+ * - effectp->trigger.interval
+ * - effectp->replay.delay
+ *
+ * @param effectp Output parameter for the effect
+ * @param from Effect to reconfigure, `NULL` to create a new effect
+ * @param direction The direction of the force feedback (if directional), where
+ * values are measure clockwise and both 0 and 1 are upwards
+ * @param length The number of milliseconds the effect is played
+ * @param effect Effect specific details (one of each axis):
+ * - .right_saturation Maximum level at end of axis, unsigned 16-bit integer
+ * - .left_saturation Maximum level at beginning of axis, unsigned 16-bit integer
+ * - .right_coeff Growth coefficient after the dead zone, signed 16-bit integer
+ * - .left_coeff Growth coefficient before the dead zone, signed 16-bit integer
+ * - .deadband Size of the dead zone
+ * - .center The position of the center of the dead zone
+ */
+inline void
+libgamepad_construct_spring_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
+ double direction, uint16_t length, const struct ff_condition_effect effect[2])
+{
+ libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_SPRING);
+ memcpy(effectp->u.condition, effect, sizeof(effectp->u.condition));
+}
+
+/**
+ * Construct a friction force-feedback effect
+ *
+ * Requires support for `FF_FRICTION` (check with
+ * `libgamepad_is_force_feedback_effect_supported`)
+ *
+ * This function will set the following values to
+ * zero if `from` is `NULL`, see <linux/input.h>
+ * for more details:
+ * - effectp->trigger.button
+ * - effectp->trigger.interval
+ * - effectp->replay.delay
+ *
+ * @param effectp Output parameter for the effect
+ * @param from Effect to reconfigure, `NULL` to create a new effect
+ * @param direction The direction of the force feedback (if directional), where
+ * values are measure clockwise and both 0 and 1 are upwards
+ * @param length The number of milliseconds the effect is played
+ * @param effect Effect specific details (one of each axis):
+ * - .right_saturation Maximum level at end of axis, unsigned 16-bit integer
+ * - .left_saturation Maximum level at beginning of axis, unsigned 16-bit integer
+ * - .right_coeff Growth coefficient after the dead zone, signed 16-bit integer
+ * - .left_coeff Growth coefficient before the dead zone, signed 16-bit integer
+ * - .deadband Size of the dead zone
+ * - .center The position of the center of the dead zone
+ */
+inline void
+libgamepad_construct_friction_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
+ double direction, uint16_t length, const struct ff_condition_effect effect[2])
+{
+ libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_FRICTION);
+ memcpy(effectp->u.condition, effect, sizeof(effectp->u.condition));
+}
+
+/**
+ * Construct a damper force-feedback effect
+ *
+ * Requires support for `FF_DAMPER` (check with
+ * `libgamepad_is_force_feedback_effect_supported`)
+ *
+ * This function will set the following values to
+ * zero if `from` is `NULL`, see <linux/input.h>
+ * for more details:
+ * - effectp->trigger.button
+ * - effectp->trigger.interval
+ * - effectp->replay.delay
+ *
+ * @param effectp Output parameter for the effect
+ * @param from Effect to reconfigure, `NULL` to create a new effect
+ * @param direction The direction of the force feedback (if directional), where
+ * values are measure clockwise and both 0 and 1 are upwards
+ * @param length The number of milliseconds the effect is played
+ * @param effect Effect specific details (one of each axis):
+ * - .right_saturation Maximum level at end of axis, unsigned 16-bit integer
+ * - .left_saturation Maximum level at beginning of axis, unsigned 16-bit integer
+ * - .right_coeff Growth coefficient after the dead zone, signed 16-bit integer
+ * - .left_coeff Growth coefficient before the dead zone, signed 16-bit integer
+ * - .deadband Size of the dead zone
+ * - .center The position of the center of the dead zone
+ */
+inline void
+libgamepad_construct_damper_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
+ double direction, uint16_t length, const struct ff_condition_effect effect[2])
+{
+ libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_DAMPER);
+ memcpy(effectp->u.condition, effect, sizeof(effectp->u.condition));
+}
+
+/**
+ * Construct a inertia force-feedback effect
+ *
+ * Requires support for `FF_INERTIA` (check with
+ * `libgamepad_is_force_feedback_effect_supported`)
+ *
+ * This function will set the following values to
+ * zero if `from` is `NULL`, see <linux/input.h>
+ * for more details:
+ * - effectp->trigger.button
+ * - effectp->trigger.interval
+ * - effectp->replay.delay
+ *
+ * @param effectp Output parameter for the effect
+ * @param from Effect to reconfigure, `NULL` to create a new effect
+ * @param direction The direction of the force feedback (if directional), where
+ * values are measure clockwise and both 0 and 1 are upwards
+ * @param length The number of milliseconds the effect is played
+ * @param effect Effect specific details (one of each axis):
+ * - .right_saturation Maximum level at end of axis, unsigned 16-bit integer
+ * - .left_saturation Maximum level at beginning of axis, unsigned 16-bit integer
+ * - .right_coeff Growth coefficient after the dead zone, signed 16-bit integer
+ * - .left_coeff Growth coefficient before the dead zone, signed 16-bit integer
+ * - .deadband Size of the dead zone
+ * - .center The position of the center of the dead zone
+ */
+inline void
+libgamepad_construct_inertia_force_feedback_effect(struct ff_effect *effectp, const struct ff_effect *from,
+ double direction, uint16_t length, const struct ff_condition_effect effect[2])
+{
+ libgamepad_construct_force_feedback_effect__(effectp, from, direction, length, FF_INERTIA);
+ memcpy(effectp->u.condition, effect, sizeof(effectp->u.condition));
}
diff --git a/libgamepad_construct_constant_force_feedback_effect.c b/libgamepad_construct_constant_force_feedback_effect.c
new file mode 100644
index 0000000..4e2898f
--- /dev/null
+++ b/libgamepad_construct_constant_force_feedback_effect.c
@@ -0,0 +1,7 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline void
+libgamepad_construct_constant_force_feedback_effect(struct ff_effect *, const struct ff_effect *,
+ double, uint16_t, const struct ff_constant_effect *);
diff --git a/libgamepad_construct_damper_force_feedback_effect.c b/libgamepad_construct_damper_force_feedback_effect.c
new file mode 100644
index 0000000..1b93b3f
--- /dev/null
+++ b/libgamepad_construct_damper_force_feedback_effect.c
@@ -0,0 +1,7 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline void
+libgamepad_construct_damper_force_feedback_effect(struct ff_effect *, const struct ff_effect *,
+ double, uint16_t, const struct ff_condition_effect[2]);
diff --git a/libgamepad_construct_force_feedback_effect__.c b/libgamepad_construct_force_feedback_effect__.c
new file mode 100644
index 0000000..f84314d
--- /dev/null
+++ b/libgamepad_construct_force_feedback_effect__.c
@@ -0,0 +1,24 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+void
+libgamepad_construct_force_feedback_effect__(struct ff_effect *effectp, const struct ff_effect *from,
+ double direction, uint16_t length, uint16_t effect)
+{
+ if (from) {
+ if (effectp != from)
+ memcpy(effectp, from, sizeof(*effectp));
+ } else {
+ memset(effectp, 0, sizeof(*effectp));
+ effectp->id = -1;
+ }
+ direction += 0.5;
+ fmod(direction, 1);
+ if (direction < 0)
+ direction = 1 - direction;
+ direction *= 0x10000UL;
+ effectp->direction = (uint16_t)direction;
+ effectp->replay.length = length;
+ effectp->type = effect;
+}
diff --git a/libgamepad_construct_friction_force_feedback_effect.c b/libgamepad_construct_friction_force_feedback_effect.c
new file mode 100644
index 0000000..6476900
--- /dev/null
+++ b/libgamepad_construct_friction_force_feedback_effect.c
@@ -0,0 +1,7 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline void
+libgamepad_construct_friction_force_feedback_effect(struct ff_effect *, const struct ff_effect *,
+ double, uint16_t, const struct ff_condition_effect[2]);
diff --git a/libgamepad_construct_inertia_force_feedback_effect.c b/libgamepad_construct_inertia_force_feedback_effect.c
new file mode 100644
index 0000000..284950d
--- /dev/null
+++ b/libgamepad_construct_inertia_force_feedback_effect.c
@@ -0,0 +1,7 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline void
+libgamepad_construct_inertia_force_feedback_effect(struct ff_effect *, const struct ff_effect *,
+ double, uint16_t, const struct ff_condition_effect[2]);
diff --git a/libgamepad_construct_periodic_force_feedback_effect.c b/libgamepad_construct_periodic_force_feedback_effect.c
new file mode 100644
index 0000000..dac529b
--- /dev/null
+++ b/libgamepad_construct_periodic_force_feedback_effect.c
@@ -0,0 +1,7 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline void
+libgamepad_construct_periodic_force_feedback_effect(struct ff_effect *, const struct ff_effect *,
+ double, uint16_t, const struct ff_periodic_effect *);
diff --git a/libgamepad_construct_ramp_force_feedback_effect.c b/libgamepad_construct_ramp_force_feedback_effect.c
new file mode 100644
index 0000000..9fbb584
--- /dev/null
+++ b/libgamepad_construct_ramp_force_feedback_effect.c
@@ -0,0 +1,7 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline void
+libgamepad_construct_ramp_force_feedback_effect(struct ff_effect *, const struct ff_effect *,
+ double, uint16_t, const struct ff_ramp_effect *);
diff --git a/libgamepad_construct_rumble_force_feedback_effect.c b/libgamepad_construct_rumble_force_feedback_effect.c
new file mode 100644
index 0000000..122d53d
--- /dev/null
+++ b/libgamepad_construct_rumble_force_feedback_effect.c
@@ -0,0 +1,7 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline void
+libgamepad_construct_rumble_force_feedback_effect(struct ff_effect *, const struct ff_effect *,
+ double, uint16_t, const struct ff_rumble_effect *);
diff --git a/libgamepad_construct_spring_force_feedback_effect.c b/libgamepad_construct_spring_force_feedback_effect.c
new file mode 100644
index 0000000..9f996b3
--- /dev/null
+++ b/libgamepad_construct_spring_force_feedback_effect.c
@@ -0,0 +1,7 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline void
+libgamepad_construct_spring_force_feedback_effect(struct ff_effect *, const struct ff_effect *,
+ double, uint16_t, const struct ff_condition_effect[2]);
diff --git a/libgamepad_disable_force_feedback_autocenter.c b/libgamepad_disable_force_feedback_autocenter.c
new file mode 100644
index 0000000..0db66bf
--- /dev/null
+++ b/libgamepad_disable_force_feedback_autocenter.c
@@ -0,0 +1,5 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline int libgamepad_disable_force_feedback_autocenter(struct libgamepad_device *);
diff --git a/libgamepad_get_absolute_axis_info.c b/libgamepad_get_absolute_axis_info.c
index 8527281..a6f8220 100644
--- a/libgamepad_get_absolute_axis_info.c
+++ b/libgamepad_get_absolute_axis_info.c
@@ -2,4 +2,4 @@
#include "common.h"
-extern inline const struct input_absinfo *libgamepad_get_absolute_axis_info(struct libgamepad_device *, unsigned int);
+extern inline const struct input_absinfo *libgamepad_get_absolute_axis_info(struct libgamepad_device *, uint16_t);
diff --git a/libgamepad_get_absolute_axis_name.c b/libgamepad_get_absolute_axis_name.c
index 3a103f4..2146c8a 100644
--- a/libgamepad_get_absolute_axis_name.c
+++ b/libgamepad_get_absolute_axis_name.c
@@ -2,4 +2,4 @@
#include "common.h"
-extern inline const char *libgamepad_get_absolute_axis_name(unsigned int);
+extern inline const char *libgamepad_get_absolute_axis_name(uint16_t);
diff --git a/libgamepad_get_button_is_pressed.c b/libgamepad_get_button_is_pressed.c
index 46a028e..7ec2f73 100644
--- a/libgamepad_get_button_is_pressed.c
+++ b/libgamepad_get_button_is_pressed.c
@@ -2,4 +2,4 @@
#include "common.h"
-extern inline int libgamepad_get_button_is_pressed(struct libgamepad_device *, unsigned int);
+extern inline int libgamepad_get_button_is_pressed(struct libgamepad_device *, uint16_t);
diff --git a/libgamepad_get_button_name.c b/libgamepad_get_button_name.c
index 9ff424d..8e7a07f 100644
--- a/libgamepad_get_button_name.c
+++ b/libgamepad_get_button_name.c
@@ -2,4 +2,4 @@
#include "common.h"
-extern inline const char *libgamepad_get_button_name(unsigned int);
+extern inline const char *libgamepad_get_button_name(uint16_t);
diff --git a/libgamepad_get_force_feedback_max_concurrency.c b/libgamepad_get_force_feedback_max_concurrency.c
new file mode 100644
index 0000000..d3db5d2
--- /dev/null
+++ b/libgamepad_get_force_feedback_max_concurrency.c
@@ -0,0 +1,5 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline int libgamepad_get_force_feedback_max_concurrency(struct libgamepad_device *);
diff --git a/libgamepad_get_relative_axis_name.c b/libgamepad_get_relative_axis_name.c
index 3867216..f3e4a46 100644
--- a/libgamepad_get_relative_axis_name.c
+++ b/libgamepad_get_relative_axis_name.c
@@ -2,4 +2,4 @@
#include "common.h"
-extern inline const char *libgamepad_get_relative_axis_name(unsigned int);
+extern inline const char *libgamepad_get_relative_axis_name(uint16_t);
diff --git a/libgamepad_install_force_feedback_effect.c b/libgamepad_install_force_feedback_effect.c
new file mode 100644
index 0000000..ab41987
--- /dev/null
+++ b/libgamepad_install_force_feedback_effect.c
@@ -0,0 +1,5 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline int libgamepad_install_force_feedback_effect(struct libgamepad_device *, struct ff_effect *);
diff --git a/libgamepad_is_force_feedback_effect_supported.c b/libgamepad_is_force_feedback_effect_supported.c
new file mode 100644
index 0000000..e32f045
--- /dev/null
+++ b/libgamepad_is_force_feedback_effect_supported.c
@@ -0,0 +1,5 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline int libgamepad_is_force_feedback_effect_supported(struct libgamepad_device *, uint16_t);
diff --git a/libgamepad_open_device.c b/libgamepad_open_device.c
index 5b32658..87868cb 100644
--- a/libgamepad_open_device.c
+++ b/libgamepad_open_device.c
@@ -16,6 +16,7 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
devicep->buttons = NULL;
devicep->absolute_axes = NULL;
devicep->relative_axes = NULL;
+ memset(devicep->force_feedback_support, 0, sizeof(devicep->force_feedback_support));
if (path && *path) {
devicep->fd = openat(dirfd, path, mode);
@@ -63,6 +64,10 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
devicep->relative_axis_map[i] = (int16_t)devicep->nrelative_axes++;
}
+ for (i = 0; i < FF_CNT; i++)
+ if (libevdev_has_event_code(devicep->dev, EV_FF, i))
+ devicep->force_feedback_support[i / 8] |= (uint8_t)(1 << (i & 7));
+
if (devicep->nbuttons) {
devicep->buttons = calloc(devicep->nbuttons, sizeof(*devicep->buttons));
if (!devicep->buttons)
diff --git a/libgamepad_play_force_feedback_effect.c b/libgamepad_play_force_feedback_effect.c
new file mode 100644
index 0000000..4d3fe00
--- /dev/null
+++ b/libgamepad_play_force_feedback_effect.c
@@ -0,0 +1,5 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline int libgamepad_play_force_feedback_effect(struct libgamepad_device *, const struct ff_effect *);
diff --git a/libgamepad_set_force_feedback_autocenter.c b/libgamepad_set_force_feedback_autocenter.c
new file mode 100644
index 0000000..fe36cca
--- /dev/null
+++ b/libgamepad_set_force_feedback_autocenter.c
@@ -0,0 +1,5 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline int libgamepad_set_force_feedback_autocenter(struct libgamepad_device *, uint16_t);
diff --git a/libgamepad_set_force_feedback_master_gain.c b/libgamepad_set_force_feedback_master_gain.c
new file mode 100644
index 0000000..72b8684
--- /dev/null
+++ b/libgamepad_set_force_feedback_master_gain.c
@@ -0,0 +1,5 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline int libgamepad_set_force_feedback_master_gain(struct libgamepad_device *, uint16_t);
diff --git a/libgamepad_stop_force_feedback_effect.c b/libgamepad_stop_force_feedback_effect.c
new file mode 100644
index 0000000..ddfdc2c
--- /dev/null
+++ b/libgamepad_stop_force_feedback_effect.c
@@ -0,0 +1,5 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline int libgamepad_stop_force_feedback_effect(struct libgamepad_device *, const struct ff_effect *);
diff --git a/libgamepad_uninstall_force_feedback_effect.c b/libgamepad_uninstall_force_feedback_effect.c
new file mode 100644
index 0000000..a71bc20
--- /dev/null
+++ b/libgamepad_uninstall_force_feedback_effect.c
@@ -0,0 +1,5 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline int libgamepad_uninstall_force_feedback_effect(struct libgamepad_device *, struct ff_effect *);
diff --git a/libgamepad_update_force_feedback_effect.c b/libgamepad_update_force_feedback_effect.c
new file mode 100644
index 0000000..17ce953
--- /dev/null
+++ b/libgamepad_update_force_feedback_effect.c
@@ -0,0 +1,5 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+
+extern inline int libgamepad_update_force_feedback_effect(struct libgamepad_device *, struct ff_effect *);
diff --git a/test-details.c b/test-details.c
index dfff98f..6ccfbe6 100644
--- a/test-details.c
+++ b/test-details.c
@@ -39,7 +39,7 @@ main(int argc, char *argv[])
assert(gamepad.button_map[gamepad.buttons[i]] == (int16_t)i);
printf("\t%s [pressed=%i]\n",
libgamepad_get_button_name(gamepad.buttons[i]),
- libgamepad_get_button_is_pressed(&gamepad, (unsigned int)gamepad.buttons[i]));
+ libgamepad_get_button_is_pressed(&gamepad, gamepad.buttons[i]));
}
n = 0;
for (i = 0; i < ELEMSOF(gamepad.button_map); i++) {
diff --git a/test-visual.c b/test-visual.c
index 58ba20d..316299c 100644
--- a/test-visual.c
+++ b/test-visual.c
@@ -24,12 +24,21 @@ draw_axis(char buffer[], const struct abs_axis *axis, size_t len)
/* TODO axis->value can actually be out of bounds */
char value_buf[sizeof(intmax_t) * 3 + 2];
size_t width, x, value_len;
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wformat-overflow"
+#endif
sprintf(buffer, "%-*ji%ji", (int)(len - axis->max_len), axis->min, axis->max);
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif
width = len - axis->max_len - axis->min_len;
value_len = (size_t)sprintf(value_buf, "%ji", axis->value);
x = axis->max_len + (width - value_len) / 2;
memcpy(&buffer[x], value_buf, value_len);
- x = ((axis->value - axis->min) * len + (axis->max - axis->min) / 2) / (axis->max - axis->min);
+ x = (size_t)(axis->value - axis->min) * len;
+ x += (size_t)(axis->max - axis->min) / 2;
+ x /= (size_t)(axis->max - axis->min);
memmove(&buffer[x + sizeof("\033[m") - 1], &buffer[x], len - x);
memcpy(&buffer[x], "\033[m", sizeof("\033[m") - 1);
return buffer;
@@ -129,13 +138,13 @@ main(int argc, char *argv[])
x = i % button_columns;
y = i / button_columns;
printf("\033[%zu;%zuH%s¿%s?\033[m", y + 1, x * (max_button_name_len + 3) + 1,
- libgamepad_get_button_is_pressed(&gamepad, (unsigned int)gamepad.buttons[i]) ? "\033[7m" : "\033[m",
+ libgamepad_get_button_is_pressed(&gamepad, gamepad.buttons[i]) ? "\033[7m" : "\033[m",
button_names[i]);
}
printf("\033[%zuH", button_rows + 1);
for (i = 0; i < gamepad.nabsolute_axes; i++) {
printf("\n%-*s ¿\033[7m%s?", (int)max_abs_axis_name_len,
- libgamepad_get_absolute_axis_name((unsigned int)gamepad.absolute_axes[i]),
+ libgamepad_get_absolute_axis_name(gamepad.absolute_axes[i]),
draw_axis(buffer, &abs_axes[i], axis_len));
}