/* See LICENSE file for copyright and license details. */ #ifndef LIBGAMEPAD_H #define LIBGAMEPAD_H #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 */ int bus_type; /** * Vendor ID for the device (sub- or superdevice) */ int vendor; /** * Product ID for the device (sub- or superdevice) */ int product; /** * Product version ID for the device (sub- or superdevice) */ 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; /** * 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]; /** * 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; }; /** * 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 */ 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(unsigned int code) { return libevdev_event_code_get_name(EV_KEY, code); } /** * Get the name of an absolute axis * * @param code The axis * @return The axis' name */ inline const char * libgamepad_get_absolute_axis_name(unsigned int code) { return libevdev_event_code_get_name(EV_ABS, code); } /** * Get the name of a relative axis * * @param code The axis * @return The axis' name */ inline const char * libgamepad_get_relative_axis_name(unsigned int code) { return libevdev_event_code_get_name(EV_REL, 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, unsigned int code) { return libevdev_get_event_value(device->dev, EV_KEY, 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, unsigned int code) { return libevdev_get_abs_info(device->dev, 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 * * 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 * 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; } /** * Ungrab a subdevice, unless not currently grabbed * * @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; } /** * 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 * * 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; } #endif