aboutsummaryrefslogtreecommitdiffstats
path: root/libgamepad.h
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 /libgamepad.h
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>
Diffstat (limited to 'libgamepad.h')
-rw-r--r--libgamepad.h542
1 files changed, 493 insertions, 49 deletions
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));
}