diff options
31 files changed, 1596 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..228cddc --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +*\#* +*~ +*.o +*.a +*.lo +*.su +*.so +*.so.* +*.dll +*.dylib +*.gch +*.gcov +*.gcno +*.gcda +/test-* +!/test-*.c @@ -0,0 +1,15 @@ +ISC License + +© 2022 Mattias Andrée <maandree@kth.se> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0ccc316 --- /dev/null +++ b/Makefile @@ -0,0 +1,98 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +OS = linux +# Linux: linux +# Mac OS: macos +# Windows: windows +include mk/$(OS).mk + + +LIB_MAJOR = 1 +LIB_MINOR = 0 +LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR) +LIB_NAME = gamepad + + +OBJ =\ + libgamepad_close_device.o\ + libgamepad_close_superdevice.o\ + libgamepad_create_attachment_monitor.o\ + libgamepad_destroy_attachment_monitor.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_relative_axis_name.o\ + libgamepad_grab.o\ + libgamepad_list_superdevices.o\ + libgamepad_next_event.o\ + libgamepad_open_device.o\ + libgamepad_open_superdevice.o\ + libgamepad_set_clock.o\ + libgamepad_ungrab.o + +HDR =\ + common.h\ + libgamepad.h + +TESTS =\ + test-attachments\ + test-details\ + test-input\ + test-list + +LOBJ = $(OBJ:.o=.lo) + + +all: libgamepad.a libgamepad.$(LIBEXT) $(TESTS) +$(OBJ): $(HDR) +$(LOBJ): $(HDR) +$(TESTS:=.o): $(HDR) +$(TESTS): libgamepad.a + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) $(LIBS_CFLAGS) + +.c.lo: + $(CC) -fPIC -c -o $@ $< $(CFLAGS) $(CPPFLAGS) $(LIBS_CFLAGS) + +.o: + $(CC) -o $@ $< libgamepad.a $(LDFLAGS) $(LIBS_LDFLAGS) + +libgamepad.a: $(OBJ) + @rm -f -- $@ + $(AR) rc $@ $(OBJ) + $(AR) ts $@ > /dev/null + +libgamepad.$(LIBEXT): $(LOBJ) + $(CC) $(LIBFLAGS) -o $@ $(LOBJ) $(LDFLAGS) $(LIBS_LDFLAGS) + +install: libgamepad.a libgamepad.$(LIBEXT) + mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" + mkdir -p -- "$(DESTDIR)$(PREFIX)/include" + cp -- libgamepad.a "$(DESTDIR)$(PREFIX)/lib/" + cp -- libgamepad.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/libgamepad.$(LIBMINOREXT)" + $(FIX_INSTALL_NAME) "$(DESTDIR)$(PREFIX)/lib/libgamepad.$(LIBMINOREXT)" + ln -sf -- libgamepad.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/libgamepad.$(LIBMAJOREXT)" + ln -sf -- libgamepad.$(LIBMAJOREXT) "$(DESTDIR)$(PREFIX)/lib/libgamepad.$(LIBEXT)" + cp -- libgamepad.h "$(DESTDIR)$(PREFIX)/include/" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libgamepad.a" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libgamepad.$(LIBMAJOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libgamepad.$(LIBMINOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libgamepad.$(LIBEXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/include/libgamepad.h" + +clean: + -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.dll *.dylib + -rm -f -- *.gch *.gcov *.gcno *.gcda *.$(LIBEXT) $(TESTS) + +.SUFFIXES: +.SUFFIXES: .lo .o .c + +.PHONY: all install uninstall clean @@ -0,0 +1,7 @@ +Add support for quirks +Add support for battery +Add support for leds +Add support for force feedback +Add support for audio +Add man pages +Add readme file diff --git a/common.h b/common.h new file mode 100644 index 0000000..09f6193 --- /dev/null +++ b/common.h @@ -0,0 +1,24 @@ +/* See LICENSE file for copyright and license details. */ +#include "libgamepad.h" + +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <libevdev/libevdev.h> +#include <libudev.h> + + +struct libgamepad_attachment_monitor { + struct udev *udev; + struct udev_monitor *monitor; +}; diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..77acc18 --- /dev/null +++ b/config.mk @@ -0,0 +1,11 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CC = c99 + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CFLAGS = -Wall -g +LDFLAGS = + +LIBS_CFLAGS = $$(pkg-config --cflags libevdev) +LIBS_LDFLAGS = $$(pkg-config --libs libevdev) -ludev diff --git a/libgamepad.h b/libgamepad.h new file mode 100644 index 0000000..627589a --- /dev/null +++ b/libgamepad.h @@ -0,0 +1,566 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBGAMEPAD_H +#define LIBGAMEPAD_H + +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stddef.h> +#include <time.h> + +#include <libevdev/libevdev.h> + + +/** + * 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 <linux/input.h> + */ + 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 <linux/input.h> */ +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 diff --git a/libgamepad_close_device.c b/libgamepad_close_device.c new file mode 100644 index 0000000..edb8361 --- /dev/null +++ b/libgamepad_close_device.c @@ -0,0 +1,22 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +void +libgamepad_close_device(struct libgamepad_device *device) +{ + if (device) { + if (device->close_fd) + close(device->fd); + if (device->dev) + libevdev_free(device->dev); + free(device->buttons); + free(device->absolute_axes); + free(device->relative_axes); + device->close_fd = 0; + device->dev = NULL; + device->buttons = NULL; + device->absolute_axes = NULL; + device->relative_axes = NULL; + } +} diff --git a/libgamepad_close_superdevice.c b/libgamepad_close_superdevice.c new file mode 100644 index 0000000..dc212bd --- /dev/null +++ b/libgamepad_close_superdevice.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +void +libgamepad_close_superdevice(struct libgamepad_superdevice *device) +{ + if (device) { + free(device->syspath); + while (device->ndevices) + free(device->devices[--device->ndevices]); + free(device->devices); + while (device->nleds) + free(device->leds[--device->nleds]); + free(device->leds); + while (device->npower_supplies) + free(device->power_supplies[--device->npower_supplies]); + free(device->power_supplies); + } +} diff --git a/libgamepad_create_attachment_monitor.c b/libgamepad_create_attachment_monitor.c new file mode 100644 index 0000000..20423b0 --- /dev/null +++ b/libgamepad_create_attachment_monitor.c @@ -0,0 +1,33 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libgamepad_create_attachment_monitor(LIBGAMEPAD_ATTACHMENT_MONITOR **monitorp) +{ + *monitorp = calloc(1, sizeof(*monitorp)); + if (!*monitorp) + return -1; + + (*monitorp)->udev = udev_new(); + if (!(*monitorp)->udev) + goto fail; + + (*monitorp)->monitor = udev_monitor_new_from_netlink((*monitorp)->udev, "udev"); + if (!(*monitorp)->monitor) + goto fail_have_udev; + + if (udev_monitor_enable_receiving((*monitorp)->monitor)) + goto fail_have_monitor; + + return udev_monitor_get_fd((*monitorp)->monitor); + +fail_have_monitor: + udev_monitor_unref((*monitorp)->monitor); +fail_have_udev: + udev_unref((*monitorp)->udev); +fail: + free(*monitorp); + *monitorp = NULL; + return -1; +} diff --git a/libgamepad_destroy_attachment_monitor.c b/libgamepad_destroy_attachment_monitor.c new file mode 100644 index 0000000..bdddb83 --- /dev/null +++ b/libgamepad_destroy_attachment_monitor.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +void +libgamepad_destroy_attachment_monitor(LIBGAMEPAD_ATTACHMENT_MONITOR *monitor) +{ + if (monitor) { + if (monitor->monitor) + udev_monitor_unref(monitor->monitor); + if (monitor->udev) + udev_unref(monitor->udev); + free(monitor); + } +} diff --git a/libgamepad_get_absolute_axis_info.c b/libgamepad_get_absolute_axis_info.c new file mode 100644 index 0000000..8527281 --- /dev/null +++ b/libgamepad_get_absolute_axis_info.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +extern inline const struct input_absinfo *libgamepad_get_absolute_axis_info(struct libgamepad_device *, unsigned int); diff --git a/libgamepad_get_absolute_axis_name.c b/libgamepad_get_absolute_axis_name.c new file mode 100644 index 0000000..3a103f4 --- /dev/null +++ b/libgamepad_get_absolute_axis_name.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +extern inline const char *libgamepad_get_absolute_axis_name(unsigned int); diff --git a/libgamepad_get_attachment_event.c b/libgamepad_get_attachment_event.c new file mode 100644 index 0000000..4462356 --- /dev/null +++ b/libgamepad_get_attachment_event.c @@ -0,0 +1,60 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libgamepad_get_attachment_event(LIBGAMEPAD_ATTACHMENT_MONITOR *monitor, char **syspathp, size_t *sizep, + enum libgamepad_attachment_event_type *typep) +{ + struct udev_device *device; + const char *action; + const char *syspath; + size_t len; + void *new; + char *syspath_end; + int saved_errno; + + device = udev_monitor_receive_device(monitor->monitor); + if (!device) + return -1; + + action = udev_device_get_action(device); + if (!strcmp(action, "bind")) + *typep = LIBGAMEPAD_ADDED; + else if (!strcmp(action, "unbind")) + *typep = LIBGAMEPAD_REMOVED; + else + goto suppress; + + syspath = udev_device_get_syspath(device); + if (!syspath || !*syspath) + goto suppress; + len = strlen(syspath) + sizeof("/hidraw"); + if (len > *sizep) { + new = realloc(*syspathp, len); + if (!new) { + udev_device_unref(device); + return -1; + } + *syspathp = new; + *sizep = len; + } + syspath_end = stpcpy(*syspathp, syspath); + + if (*typep == LIBGAMEPAD_ADDED) { + stpcpy(syspath_end, "/hidraw"); + saved_errno = errno; + if (access(*syspathp, F_OK)) { + errno = saved_errno; + goto suppress; + } + *syspath_end = '\0'; + } + + udev_device_unref(device); + return 1; + +suppress: + udev_device_unref(device); + return 0; +} diff --git a/libgamepad_get_button_is_pressed.c b/libgamepad_get_button_is_pressed.c new file mode 100644 index 0000000..46a028e --- /dev/null +++ b/libgamepad_get_button_is_pressed.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +extern inline int libgamepad_get_button_is_pressed(struct libgamepad_device *, unsigned int); diff --git a/libgamepad_get_button_name.c b/libgamepad_get_button_name.c new file mode 100644 index 0000000..9ff424d --- /dev/null +++ b/libgamepad_get_button_name.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +extern inline const char *libgamepad_get_button_name(unsigned int); diff --git a/libgamepad_get_relative_axis_name.c b/libgamepad_get_relative_axis_name.c new file mode 100644 index 0000000..3867216 --- /dev/null +++ b/libgamepad_get_relative_axis_name.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +extern inline const char *libgamepad_get_relative_axis_name(unsigned int); diff --git a/libgamepad_grab.c b/libgamepad_grab.c new file mode 100644 index 0000000..a6a6a41 --- /dev/null +++ b/libgamepad_grab.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +extern inline int libgamepad_grab(struct libgamepad_device *); diff --git a/libgamepad_list_superdevices.c b/libgamepad_list_superdevices.c new file mode 100644 index 0000000..f8c7c4d --- /dev/null +++ b/libgamepad_list_superdevices.c @@ -0,0 +1,77 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libgamepad_list_superdevices(char ***devicesp, size_t *ndevicesp) +{ + char path[PATH_MAX], target[PATH_MAX]; /* TODO do not use PATH_MAX */ + int dirfd; + DIR *dir; + struct dirent *f; + ssize_t r; + char **paths = NULL; + size_t n = 0, i; + void *new; + int saved_errno = errno; + + *devicesp = NULL; + *ndevicesp = 0; + + dirfd = open("/sys/class/input/", O_DIRECTORY); + if (dirfd < 0) + return -1; + + dir = fdopendir(dirfd); + if (!dir) { + close(dirfd); + return -1; + } + + while (errno = 0, (f = readdir(dir))) { + if (!strncmp(f->d_name, "event", 5) && isdigit(f->d_name[5])) { + stpcpy(stpcpy(stpcpy(path, "/sys/class/input/"), f->d_name), "/device/device"); + dirfd = open(path, O_PATH); + if (dirfd < 0) + continue; + sprintf(path, "/dev/fd/%i", dirfd); + r = readlink(path, target, sizeof(target) - 1); + close(dirfd); + if (r < 0) + continue; + target[r] = '\0'; + if (paths) + for (i = 0; i < n; i++) + if (!strcmp(paths[i], target)) + goto next; + stpcpy(stpcpy(path, target), "/input"); + if (access(path, F_OK)) + continue; + new = realloc(paths, (n + 1) * sizeof(*paths)); + if (!new) + goto fail; + paths = new; + paths[n] = strdup(target); + if (!paths[n]) + goto fail; + n += 1; + } + next:; + } + + if (errno) + goto fail; + closedir(dir); + + errno = saved_errno; + *devicesp = paths; + *ndevicesp = n; + return 0; + +fail: + for (i = 0; i < n; i++) + free(paths[i]); + free(paths); + closedir(dir); + return -1; +} diff --git a/libgamepad_next_event.c b/libgamepad_next_event.c new file mode 100644 index 0000000..acc23ea --- /dev/null +++ b/libgamepad_next_event.c @@ -0,0 +1,57 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libgamepad_next_event(struct libgamepad_device *device, struct libgamepad_input_event *eventp) +{ + int r; + struct input_event ev; + + if (!device->require_sync) { + r = libevdev_next_event(device->dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + if (r < 0) { + errno = -r; + return -1; + } + } else { + r = libevdev_next_event(device->dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + if (r == -EAGAIN) { + device->require_sync = 0; /* yes, this is how it is document */ + return 0; + } else if (r < 0) { + errno = -r; + return -1; + } + if (r != LIBEVDEV_READ_STATUS_SYNC) /* just to be safe */ + device->require_sync = 0; + } + + eventp->sync_event = (r == LIBEVDEV_READ_STATUS_SYNC); + eventp->code = ev.code; + eventp->value = ev.value; + eventp->time.tv_sec = ev.input_event_sec; + eventp->time.tv_usec = ev.input_event_usec; + + switch (ev.type) { + case EV_KEY: + eventp->type = LIBGAMEPAD_BUTTON; + return 1; + + case EV_ABS: + eventp->type = LIBGAMEPAD_ABSOLUTE_AXIS; + return 1; + + case EV_REL: + eventp->type = LIBGAMEPAD_RELATIVE_AXIS; + return 1; + + case EV_SYN: + if (ev.code == SYN_DROPPED) + device->require_sync = 1; + return 0; + + default: + return 0; + } +} diff --git a/libgamepad_open_device.c b/libgamepad_open_device.c new file mode 100644 index 0000000..5b32658 --- /dev/null +++ b/libgamepad_open_device.c @@ -0,0 +1,98 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char *path, int mode) +{ + int err; + unsigned int i; + uint16_t n; + + devicep->fd = dirfd; + devicep->close_fd = 0; + devicep->require_sync = 0; + devicep->dev = NULL; + devicep->buttons = NULL; + devicep->absolute_axes = NULL; + devicep->relative_axes = NULL; + + if (path && *path) { + devicep->fd = openat(dirfd, path, mode); + if (devicep->fd < 0) + return -1; + devicep->close_fd = 1; + } + + err = -libevdev_new_from_fd(devicep->fd, &devicep->dev); + if (err > 0) { + if (devicep->close_fd) { + close(devicep->fd); + devicep->close_fd = 0; + } + errno = err; + return -1; + } + + devicep->name = libevdev_get_name(devicep->dev); + devicep->unique_id = libevdev_get_uniq(devicep->dev); + devicep->physical_location = libevdev_get_phys(devicep->dev); + devicep->bus_type = libevdev_get_id_bustype(devicep->dev); + devicep->vendor = libevdev_get_id_vendor(devicep->dev); + devicep->product = libevdev_get_id_product(devicep->dev); + devicep->version = libevdev_get_id_version(devicep->dev); + + devicep->nbuttons = 0; + for (i = 0; i < KEY_CNT; i++) { + devicep->button_map[i] = -1; + if (libevdev_has_event_code(devicep->dev, EV_KEY, i)) + devicep->button_map[i] = (int16_t)devicep->nbuttons++; + } + + devicep->nabsolute_axes = 0; + for (i = 0; i < ABS_CNT; i++) { + devicep->absolute_axis_map[i] = -1; + if (libevdev_has_event_code(devicep->dev, EV_ABS, i)) + devicep->absolute_axis_map[i] = (int16_t)devicep->nabsolute_axes++; + } + + devicep->nrelative_axes = 0; + for (i = 0; i < REL_CNT; i++) { + devicep->relative_axis_map[i] = -1; + if (libevdev_has_event_code(devicep->dev, EV_REL, i)) + devicep->relative_axis_map[i] = (int16_t)devicep->nrelative_axes++; + } + + if (devicep->nbuttons) { + devicep->buttons = calloc(devicep->nbuttons, sizeof(*devicep->buttons)); + if (!devicep->buttons) + goto fail; + for (i = 0, n = 0; i < KEY_CNT; i++) + if (devicep->button_map[i] >= 0) + devicep->buttons[n++] = (uint16_t)i; + } + + if (devicep->nabsolute_axes) { + devicep->absolute_axes = calloc(devicep->nabsolute_axes, sizeof(*devicep->absolute_axes)); + if (!devicep->absolute_axes) + goto fail; + for (i = 0, n = 0; i < ABS_CNT; i++) + if (devicep->absolute_axis_map[i] >= 0) + devicep->absolute_axes[n++] = (uint16_t)i; + } + + if (devicep->nrelative_axes) { + devicep->relative_axes = calloc(devicep->nrelative_axes, sizeof(*devicep->relative_axes)); + if (!devicep->relative_axes) + goto fail; + for (i = 0, n = 0; i < REL_CNT; i++) + if (devicep->relative_axis_map[i] >= 0) + devicep->relative_axes[n++] = (uint16_t)i; + } + + return 0; + +fail: + libgamepad_close_device(devicep); + return -1; +} diff --git a/libgamepad_open_superdevice.c b/libgamepad_open_superdevice.c new file mode 100644 index 0000000..b6b975f --- /dev/null +++ b/libgamepad_open_superdevice.c @@ -0,0 +1,149 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +int +libgamepad_open_superdevice(struct libgamepad_superdevice *devicep, const char *syspath) +{ + struct libgamepad_subdevice *subdev; + int dirfd = -1, dirfd2, dirfd3; + DIR *dir = NULL, *dir2 = NULL; + struct dirent *f, *f2; + enum libgamepad_type type; + size_t first = 0, len; + void *new; + int saved_errno; + + devicep->ndevices = 0; + devicep->devices = NULL; + devicep->nleds = 0; + devicep->leds = NULL; + devicep->npower_supplies = 0; + devicep->power_supplies = NULL; + + devicep->syspath = strdup(syspath); + if (!devicep->syspath) + return -1; + + dirfd = open(syspath, O_DIRECTORY); + if (dirfd < 0) + goto fail; + + dirfd2 = openat(dirfd, "input", O_DIRECTORY); + if (dirfd2 < 0) + goto fail; + dir = fdopendir(dirfd2); + if (!dir) + goto fail; + while (errno = 0, (f = readdir(dir))) { + if (!strncmp(f->d_name, "input", 5) && isdigit(f->d_name[5])) { + dirfd3 = openat(dirfd2, f->d_name, O_DIRECTORY); + if (dirfd3 < 0) + goto fail; + dir2 = fdopendir(dirfd3); + if (!dir2) + goto fail; + type = 0; + while (errno = 0, (f2 = readdir(dir2))) { + if (!strncmp(f2->d_name, "event", 5) && isdigit(f2->d_name[5])) { + new = realloc(devicep->devices, (devicep->ndevices + 1) * sizeof(*devicep->devices)); + if (!new) + goto fail; + devicep->devices = new; + len = sizeof("/dev/input/") + strlen(f2->d_name); + subdev = malloc(offsetof(struct libgamepad_subdevice, path) + len); + if (!subdev) + goto fail; + stpcpy(stpcpy(subdev->path, "/dev/input/"), f2->d_name); + devicep->devices[devicep->ndevices++] = subdev; + } else if (!strncmp(f2->d_name, "js", 2) && isdigit(f2->d_name[2])) { + type |= LIBGAMEPAD_GAMEPAD; + } else if (!strncmp(f2->d_name, "mouse", 5) && isdigit(f2->d_name[5])) { + type |= LIBGAMEPAD_MOUSE; + } + } + while (first < devicep->ndevices) + devicep->devices[first++]->type = type; + closedir(dir2); + dir2 = NULL; + } + } + if (errno) + goto fail; + closedir(dir); + dir = NULL; + + dirfd2 = openat(dirfd, "leds", O_DIRECTORY); + if (dirfd2 >= 0) { + dir = fdopendir(dirfd2); + if (!dir) + goto fail; + len = strlen(syspath) + sizeof("/leds/"); + while (errno = 0, (f = readdir(dir))) { + if (f->d_name[0] != '.') { + new = realloc(devicep->leds, (devicep->nleds + 1) * sizeof(*devicep->leds)); + if (!new) + goto fail; + devicep->leds = new; + devicep->leds[devicep->nleds] = malloc(len + strlen(f->d_name)); + if (!devicep->leds[devicep->nleds]) + goto fail; + stpcpy(stpcpy(stpcpy(devicep->leds[devicep->nleds], syspath), "/leds/"), f->d_name); + devicep->nleds += 1; + } + } + if (errno) + goto fail; + closedir(dir); + dir = NULL; + } + + dirfd2 = openat(dirfd, "power_supply", O_DIRECTORY); + if (dirfd2 >= 0) { + dir = fdopendir(dirfd2); + if (!dir) + goto fail; + len = strlen(syspath) + sizeof("/power_supply/"); + while (errno = 0, (f = readdir(dir))) { + if (f->d_name[0] != '.') { + new = realloc(devicep->power_supplies, + (devicep->npower_supplies + 1) * sizeof(*devicep->power_supplies)); + if (!new) + goto fail; + devicep->power_supplies = new; + devicep->power_supplies[devicep->npower_supplies] = malloc(len + strlen(f->d_name)); + if (!devicep->power_supplies[devicep->npower_supplies]) + goto fail; + stpcpy(stpcpy(stpcpy(devicep->power_supplies[devicep->npower_supplies], syspath), + "/power_supply/"), f->d_name); + devicep->npower_supplies += 1; + } + } + if (errno) + goto fail; + closedir(dir); + dir = NULL; + } + + close(dirfd); + return 0; + +fail: + saved_errno = errno; + if (dir2) + closedir(dir2); + if (dir) + closedir(dir); + if (dirfd >= 0) + close(dirfd); + libgamepad_close_superdevice(devicep); + devicep->syspath = NULL; + devicep->ndevices = 0; + devicep->devices = NULL; + devicep->nleds = 0; + devicep->leds = NULL; + devicep->npower_supplies = 0; + devicep->power_supplies = NULL; + errno = saved_errno; + return -1; +} diff --git a/libgamepad_set_clock.c b/libgamepad_set_clock.c new file mode 100644 index 0000000..312f963 --- /dev/null +++ b/libgamepad_set_clock.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +extern inline int libgamepad_set_clock(struct libgamepad_device *, clockid_t); diff --git a/libgamepad_ungrab.c b/libgamepad_ungrab.c new file mode 100644 index 0000000..18d39ac --- /dev/null +++ b/libgamepad_ungrab.c @@ -0,0 +1,5 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +extern inline int libgamepad_ungrab(struct libgamepad_device *); diff --git a/mk/linux.mk b/mk/linux.mk new file mode 100644 index 0000000..ad58f69 --- /dev/null +++ b/mk/linux.mk @@ -0,0 +1,6 @@ +LIBEXT = so +LIBFLAGS = -shared -Wl,-soname,lib$(LIB_NAME).$(LIBEXT).$(LIB_MAJOR) +LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR) +LIBMINOREXT = $(LIBEXT).$(LIB_VERSION) + +FIX_INSTALL_NAME = : diff --git a/mk/macos.mk b/mk/macos.mk new file mode 100644 index 0000000..6c0d575 --- /dev/null +++ b/mk/macos.mk @@ -0,0 +1,6 @@ +LIBEXT = dylib +LIBFLAGS = -dynamiclib -Wl,-compatibility_version,$(LIB_MAJOR) -Wl,-current_version,$(LIB_VERSION) +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = install_name_tool -id "$(PREFIX)/lib/libgamepad.$(LIBMAJOREXT)" diff --git a/mk/windows.mk b/mk/windows.mk new file mode 100644 index 0000000..ed5ec8d --- /dev/null +++ b/mk/windows.mk @@ -0,0 +1,6 @@ +LIBEXT = dll +LIBFLAGS = -shared +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = : diff --git a/test-attachments.c b/test-attachments.c new file mode 100644 index 0000000..9f8e63c --- /dev/null +++ b/test-attachments.c @@ -0,0 +1,50 @@ +/* See LICENSE file for copyright and license details. */ +#include "libgamepad.h" + +#include <sys/select.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + + +int +main(void) +{ + LIBGAMEPAD_ATTACHMENT_MONITOR *monitor; + int fd, r; + fd_set fds; + char *syspath = NULL; + size_t size = 0; + enum libgamepad_attachment_event_type action; + + fd = libgamepad_create_attachment_monitor(&monitor); + if (fd < 0) { + perror("libgamepad_create_attachment_monitor"); + return 1; + } + + FD_ZERO(&fds); + for (;;) { + FD_SET(fd, &fds); + if (select(fd + 1, &fds, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + perror("select"); + libgamepad_destroy_attachment_monitor(monitor); + free(syspath); + return 1; + } + + r = libgamepad_get_attachment_event(monitor, &syspath, &size, &action); + if (r <= 0) { + if (!r || errno == EINTR || errno == EAGAIN) + continue; + perror("libgamepad_get_attachment_event"); + libgamepad_destroy_attachment_monitor(monitor); + free(syspath); + return 1; + } + + printf("%s %s\n", action == LIBGAMEPAD_ADDED ? "added" : "removed", syspath); + } +} diff --git a/test-details.c b/test-details.c new file mode 100644 index 0000000..dfff98f --- /dev/null +++ b/test-details.c @@ -0,0 +1,101 @@ +/* See LICENSE file for copyright and license details. */ +#include "libgamepad.h" + +#include <assert.h> +#include <stdio.h> + +#define ELEMSOF(A) (sizeof(A) / sizeof(*(A))) + + +int +main(int argc, char *argv[]) +{ + const struct input_absinfo *absinfo; + struct libgamepad_device gamepad; + size_t i, n; + + if (argc != 2) { + fprintf(stderr, "Please provide the path to the subdevice as the only command line argument\n"); + return 1; + } + + if (libgamepad_open_device(&gamepad, AT_FDCWD, argv[1], O_RDONLY)) { + perror("libgamepad_open_device"); + return 1; + } + + printf("Bus type: %i\n", gamepad.bus_type); + printf("Vendor: %i\n", gamepad.vendor); + printf("Product: %i\n", gamepad.product); + printf("Version: %i\n", gamepad.version); + printf("Name: %s\n", gamepad.name); + printf("Unique id: %s\n", gamepad.unique_id); + printf("Physical location: %s\n", gamepad.physical_location); + + printf("Buttons:\n"); + for (i = 0; i < gamepad.nbuttons; i++) { + assert(gamepad.buttons); + assert(gamepad.buttons[i] < ELEMSOF(gamepad.button_map)); + 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])); + } + n = 0; + for (i = 0; i < ELEMSOF(gamepad.button_map); i++) { + assert(gamepad.button_map[i] >= -1); + assert((ssize_t)gamepad.button_map[i] < (ssize_t)gamepad.nbuttons); + if (gamepad.button_map[i] != -1) { + assert(gamepad.buttons[gamepad.button_map[i]] == (uint16_t)i); + n++; + } + } + assert(n == gamepad.nbuttons); + + printf("Absolute axes:\n"); + for (i = 0; i < gamepad.nabsolute_axes; i++) { + assert(gamepad.absolute_axes); + assert(gamepad.absolute_axes[i] < ELEMSOF(gamepad.absolute_axis_map)); + assert(gamepad.absolute_axis_map[gamepad.absolute_axes[i]] == (int16_t)i); + absinfo = libgamepad_get_absolute_axis_info(&gamepad, gamepad.absolute_axes[i]); + if (!absinfo) { + printf("\t%s\n", libgamepad_get_absolute_axis_name(gamepad.absolute_axes[i])); + } else { + printf("\t%s [value=%i, min=%i, max=%i, fuzz=%i, flat=%i, resolution=%i]\n", + libgamepad_get_absolute_axis_name(gamepad.absolute_axes[i]), + absinfo->value, absinfo->minimum, absinfo->maximum, + absinfo->fuzz, absinfo->flat, absinfo->resolution); + } + } + n = 0; + for (i = 0; i < ELEMSOF(gamepad.absolute_axis_map); i++) { + assert(gamepad.absolute_axis_map[i] >= -1); + assert((ssize_t)gamepad.absolute_axis_map[i] < (ssize_t)gamepad.nabsolute_axes); + if (gamepad.absolute_axis_map[i] != -1) { + assert(gamepad.absolute_axes[gamepad.absolute_axis_map[i]] == (uint16_t)i); + n++; + } + } + assert(n == gamepad.nabsolute_axes); + + printf("Relative axes:\n"); + for (i = 0; i < gamepad.nrelative_axes; i++) { + assert(gamepad.relative_axes); + assert(gamepad.relative_axes[i] < ELEMSOF(gamepad.relative_axis_map)); + assert(gamepad.relative_axis_map[gamepad.relative_axes[i]] == (int16_t)i); + printf("\t%s\n", libgamepad_get_relative_axis_name(gamepad.relative_axes[i])); + } + n = 0; + for (i = 0; i < ELEMSOF(gamepad.relative_axis_map); i++) { + assert(gamepad.relative_axis_map[i] >= -1); + assert((ssize_t)gamepad.relative_axis_map[i] < (ssize_t)gamepad.nrelative_axes); + if (gamepad.relative_axis_map[i] != -1) { + assert(gamepad.relative_axes[gamepad.relative_axis_map[i]] == (uint16_t)i); + n++; + } + } + assert(n == gamepad.nrelative_axes); + + libgamepad_close_device(&gamepad); + return 0; +} diff --git a/test-input.c b/test-input.c new file mode 100644 index 0000000..1fd5c4c --- /dev/null +++ b/test-input.c @@ -0,0 +1,77 @@ +/* See LICENSE file for copyright and license details. */ +#include "libgamepad.h" + +#include <assert.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +#define GRAB_DEVICE 0 + + +static struct libgamepad_device gamepad; + + +static void +sigint_handler(int signo) +{ + (void) signo; + +#if GRAB_DEVICE + if (libgamepad_ungrab(&gamepad)) + perror("libgamepad_ungrab"); +#endif + + libgamepad_close_device(&gamepad); + exit(0); +} + + +int +main(int argc, char *argv[]) +{ + struct libgamepad_input_event event; + int r; + + if (argc != 2) { + fprintf(stderr, "Please provide the path to the subdevice as the only command line argument\n"); + return 1; + } + + if (libgamepad_open_device(&gamepad, AT_FDCWD, argv[1], O_RDONLY)) { + perror("libgamepad_open_device"); + return 1; + } + +#if GRAB_DEVICE + if (libgamepad_grab(&gamepad)) + perror("libgamepad_grab"); +#endif + + if (signal(SIGINT, sigint_handler) == SIG_ERR) { + perror("signal"); + return 1; + } + + for (;;) { + r = libgamepad_next_event(&gamepad, &event); + if (r <= 0) { + if (!r || errno == EINTR) + continue; + perror("libgamepad_next_event"); + return 1; + } + printf("[%lli.%06li] ", (long long int)event.time.tv_sec, event.time.tv_usec); + if (event.sync_event) + printf("[sync] "); + if (event.type == LIBGAMEPAD_BUTTON) { + printf("%s ", libgamepad_get_button_name(event.code)); + } else if (event.type == LIBGAMEPAD_ABSOLUTE_AXIS) { + printf("%s ", libgamepad_get_absolute_axis_name(event.code)); + } else { + assert(event.type == LIBGAMEPAD_RELATIVE_AXIS); + printf("%s ", libgamepad_get_relative_axis_name(event.code)); + } + printf("%lli\n", (long long int)event.value); + } +} diff --git a/test-list.c b/test-list.c new file mode 100644 index 0000000..f604dd9 --- /dev/null +++ b/test-list.c @@ -0,0 +1,42 @@ +/* See LICENSE file for copyright and license details. */ +#include "libgamepad.h" + +#include <stdio.h> +#include <stdlib.h> + + +int +main(void) +{ + char **devices; + size_t ndevices, i, j; + struct libgamepad_superdevice device; + + if (libgamepad_list_superdevices(&devices, &ndevices)) { + perror("libgamepad_list_superdevices"); + return 1; + } + + for (i = 0; i < ndevices; i++) { + if (libgamepad_open_superdevice(&device, devices[i])) { + perror("libgamepad_open_superdevice"); + free(devices[i]); + continue; + } + free(devices[i]); + + printf("%s\n", device.syspath); + for (j = 0; j < device.ndevices; j++) + printf("\t%s (%x)\n", device.devices[j]->path, device.devices[j]->type); + for (j = 0; j < device.nleds; j++) + printf("\t%s (LED)\n", device.leds[j]); + for (j = 0; j < device.npower_supplies; j++) + printf("\t%s (PSU)\n", device.power_supplies[j]); + + libgamepad_close_superdevice(&device); + } + + free(devices); + + return 0; +} |