diff options
author | Mattias Andrée <maandree@kth.se> | 2022-07-25 12:35:05 +0200 |
---|---|---|
committer | Mattias Andrée <maandree@kth.se> | 2022-07-25 12:35:05 +0200 |
commit | 41ae3f04fd22d22c552742458344591061822884 (patch) | |
tree | 00ef785370327ebf578efa49a38c430523c4924a | |
parent | Add test-visual (diff) | |
download | libgamepad-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>
32 files changed, 671 insertions, 61 deletions
@@ -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\ @@ -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 @@ -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> @@ -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)); } |