/* See LICENSE file for copyright and license details. */ #ifndef LIBGAMEPAD_H #define LIBGAMEPAD_H #include #include #include #include #include #include #include #include #include #include /** * Opaque structure for monitoring device attachment changes */ typedef struct libgamepad_attachment_monitor LIBGAMEPAD_ATTACHMENT_MONITOR; /** * Device attachment event type */ enum libgamepad_attachment_event_type { /** * Device has been added */ LIBGAMEPAD_ADDED, /** * Device has been removed */ LIBGAMEPAD_REMOVED }; /** * Device type * * A device can have any number of these applied to * it, it may also have unknown types beyond these */ enum libgamepad_type { /** * Gamepad, joystick, steering wheel, or similar */ LIBGAMEPAD_GAMEPAD = 0x0001, /** * Computer mouse */ LIBGAMEPAD_MOUSE = 0x0002 }; /** * Gamepad input type */ enum libgamepad_input_type { /** * Button/key */ LIBGAMEPAD_BUTTON, /** * Absolute axis */ LIBGAMEPAD_ABSOLUTE_AXIS, /** * Relative axis */ LIBGAMEPAD_RELATIVE_AXIS }; /** * Subdevice input event structure */ struct libgamepad_input_event { /** * The affect input type */ enum libgamepad_input_type type; /** * Whether the event was polled in a * synchronisation read */ short int sync_event; /** * The button/key or axis affect by the event */ uint16_t code; /** * The new value on the button/key or axis, * or the delta if on a relative axis */ int32_t value; /** * Event timestamp */ struct timeval time; }; /** * Subdevice on a device * * For example a modern gamepad may be split into * a gamepad, computer mouse, and motion sensor * * Structure is always deallocated by the library * using free(3) */ struct libgamepad_subdevice { /** * Device type */ enum libgamepad_type type; /** * Device path */ char path[]; }; /** * Physical device * * Contents of structure is deallocated with * `libgamepad_close_superdevice` */ struct libgamepad_superdevice { /** * Device path in /sys */ char *syspath; /** * Number of subdevices */ size_t ndevices; /** * Subdevices */ struct libgamepad_subdevice **devices; /** * Number of LEDs */ size_t nleds; /** * Paths to LEDs */ char **leds; /** * Number of power supplies (batteries) */ size_t npower_supplies; /** * Paths of power supplies (batteries) */ char **power_supplies; }; /** * Subdevice structure for detailed information listening on input events */ struct libgamepad_device { /** * File descriptor to the device, the application * may use it to poll for read-readyness */ int fd; /** * Bus type the device is connect via, see BUS_-prefixed * constants in */ unsigned int bus_type; /** * Vendor ID for the device (sub- or superdevice) */ unsigned int vendor; /** * Product ID for the device (sub- or superdevice) */ unsigned int product; /** * Product version ID for the device (sub- or superdevice) */ unsigned int version; /** * FOR INTERNAL USE * * Specifies whether `.fd` shall be closed with the device */ int close_fd; /** * FOR INTERNAL USE * * Whether the device must be synchronised */ int require_sync; /** * Human-readable device (sub- or superdevice) name */ const char *name; /** * ID that is supposted to be unique to the device * (sub- or superdevice) */ const char *unique_id; /** * The location if the device */ const char *physical_location; /** * libevdev instance for the device */ struct libevdev *dev; /** * Number of (digital) buttons/keys present * on the device */ size_t nbuttons; /** * Number of absolute axes present on the device */ size_t nabsolute_axes; /** * Number of relative axes present on the 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`[. */ int16_t button_map[KEY_CNT]; /** * Maps from absolute axis codes to absolute axis * indices, non-present axes map to -1, other * values are in [0, `.nabsolute_axes`[. */ int16_t absolute_axis_map[ABS_CNT]; /** * Maps from relative axis codes to absolute axis * indices, non-present axes map to -1, other * values are in [0, `.nrelative_axes`[. */ int16_t relative_axis_map[REL_CNT]; /** * Bitmap of supported force feedback effects */ uint8_t force_feedback_support[(FF_CNT + 7) / 8]; }; /* FOR INTERNAL USE */ void libgamepad_construct_force_feedback_effect__(struct ff_effect *, const struct ff_effect *, double, uint16_t, uint16_t); /** * Get a list of all available physical devices * * @param devicesp Output parameter for the list of devices * @param ndevicesp Output parameter for the number of listed devices * @return 0 on success, -1 on failure * * This function may fail for any reason specified for * realloc(3), open(3), fdopendir(3), or readdir(3) */ int libgamepad_list_superdevices(char ***, size_t *); /** * Get information about a physical device * * @param devicep Output parameter for the device information, * allocated memory shall be deallocated with * `libgamepad_close_superdevice(devicep)` * @param syspath The path of the device, in /sys * @return 0 on success, -1 on failure * * This function may fail for any reason specified for * realloc(3), openat(3), fdopendir(3), or readdir(3) */ int libgamepad_open_superdevice(struct libgamepad_superdevice *, const char *); /** * Deallocate the contents of a `struct libgamepad_superdevice` * * @param device The structure whose nested memory allocations * shall be deallocated, may be `NULL` */ void libgamepad_close_superdevice(struct libgamepad_superdevice *); /** * Create a device attachment monitor * * The user shall poll the returned file descriptor * for read-readiness, and whenever it is ready, * call `libgamepad_get_attachment_event` * * @param monitorp Output parameter for the monitor, shall deallocated with * `libgamepad_destroy_attachment_monitor` when no longer used * @return A file descriptor on successful completion, -1 on failure * * This function may fail for any reason specified by * malloc(3), udev_new(3), udev_monitor_new_from_netlink(3), or * udev_monitor_enable_receiving(3) */ int libgamepad_create_attachment_monitor(LIBGAMEPAD_ATTACHMENT_MONITOR **); /** * Deallocate a device attachment monitor * * @param monitor The monitor to deallocate; the file descriptor returned * with it will also be closed and become invalid, may be `NULL` */ void libgamepad_destroy_attachment_monitor(LIBGAMEPAD_ATTACHMENT_MONITOR *); /** * Get the next device attachment event * * @param monitor Monitor created with `libgamepad_create_attachment_monitor` * @param syspathp Pointer to output buffer for the device's path in /sys; * the buffer may be reallocated by the function * @param sizep Pointer to the allocation size of `*syspath`; may be * updated by the function * @param typep Output parameter for the attachment event type; in the * event that this value is set to `LIBGAMEPAD_REMOVED`, the * device is not necessarily on supported by this library * @return 1 on success, 0 if the received event was suppressed * by the library, -1 on failure * * This function may fail for any reason specified for * realloc(3) or udev_monitor_receive_device(3); notable, * this function may set `errno` to `EAGAIN` or `EINTR` */ int libgamepad_get_attachment_event(LIBGAMEPAD_ATTACHMENT_MONITOR *, char **, size_t *, enum libgamepad_attachment_event_type *); /** * Open a subdevice * * @param devicep Output parameter for the device information and handles; * deallocated with `libgamepad_close_device` * @param dirfd File descriptor to path that `path` is relative to * (unless it is an absolute path), or `AT_FDCWD` for the * current working directory * @param path The path to the device, if `NULL` or empty, `dirfd` * will be used as the file descriptor to the device, * in which case the application must keep it open until * the device is closed, and then close it manually, and * `mode` is ignored * @param mode Mode to open the device in, normally `O_RDONLY`, * `O_RDONLY|O_NONBLOCK`, `O_RDWR`, or `O_RDWR|O_NONBLOCK` * @return 0 on success, -1 on failure * * This function may fail for any reason specified for * malloc(3), openat(3) or libevdev_new_from_fd(3) */ int libgamepad_open_device(struct libgamepad_device *, int, const char *, int); /** * Close a subdevice * * @param device Device information and handles, may be `NULL` */ void libgamepad_close_device(struct libgamepad_device *); /** * Get the next event on a subdevice * * NB! events may be queued internally (by libevdev), therefore * an application must either call this function in a loop in * its own thread, or have the device opened in non-blocking * mode and even the poll the device's file descriptor for * read-readyness and when it is ready clal this function in * a loop until it sets `errno` to `EAGAIN`. * * @param device Device to read an event from * @param eventp Output parameter for the event * @return 1 if an event was returned, * 0 if no event was returned, * -1 on failure * * This function may fail for any reason specified for * 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 or access has been revoked */ int libgamepad_next_event(struct libgamepad_device *, struct libgamepad_input_event *); /** * Get the name of a button/key * * @param code The button/key * @return The button/key's name */ inline const char * libgamepad_get_button_name(uint16_t code) { return libevdev_event_code_get_name(EV_KEY, (unsigned int)code); } /** * Get the name of an absolute axis * * @param code The axis * @return The axis' name */ inline const char * libgamepad_get_absolute_axis_name(uint16_t code) { return libevdev_event_code_get_name(EV_ABS, (unsigned int)code); } /** * Get the name of a relative axis * * @param code The axis * @return The axis' name */ inline const char * libgamepad_get_relative_axis_name(uint16_t code) { return libevdev_event_code_get_name(EV_REL, (unsigned int)code); } /** * Get whether a button/key is pressed down or not * * @param device The device to retrieve the information for * @param code The button/key * @return 1 if the button/key is pressed down, * 0 otherwise */ inline int libgamepad_get_button_is_pressed(struct libgamepad_device *device, uint16_t code) { return libevdev_get_event_value(device->dev, EV_KEY, (unsigned int)code); } /** * Get information about an absolute axis * * @param device The device to retrieve the information for * @param code The axis * @return Information about the axis */ inline const struct input_absinfo * /* `struct input_absinfo` is defined in */ libgamepad_get_absolute_axis_info(struct libgamepad_device *device, uint16_t code) { return libevdev_get_abs_info(device->dev, (unsigned int)code); } /** * Grab a subdevice, if not already grabbed * * This is supposed to block out clients from * retrieving events for the device, but it * does not necessarily stop them from reading * the device state * * @param device The device to grab * @return 0 on success, -1 on failure * * In the event that the device is already grabbed by * 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) { return ioctl(device->fd, EVIOCGRAB, (void *)1); } /** * Ungrab a subdevice, unless not currently grabbed * * @param device The device to ungrab * @return 0 on success, -1 on failure */ inline int libgamepad_ungrab(struct libgamepad_device *device) { return ioctl(device->fd, EVIOCGRAB, (void *)0); } /** * Set the clock that event timestamps shall be reported in * * @param device The device to configure * @param clockid The clock to use on event timestamps * @return 0 on success, -1 on failure */ inline int libgamepad_set_clock(struct libgamepad_device *device, clockid_t clockid) { 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 */ 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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 * 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)); } #endif