aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2022-07-27 15:32:10 +0200
committerMattias Andrée <maandree@kth.se>2022-07-27 15:32:10 +0200
commit586ddd6fad5061ea1de4b0fbe903c1c9ea582f72 (patch)
treee34b10acf215a2140df2fd3728300c34825f0734
parentnames.sh: improve readability (diff)
downloadlibgamepad-586ddd6fad5061ea1de4b0fbe903c1c9ea582f72.tar.gz
libgamepad-586ddd6fad5061ea1de4b0fbe903c1c9ea582f72.tar.bz2
libgamepad-586ddd6fad5061ea1de4b0fbe903c1c9ea582f72.tar.xz
m + add controller fingerprint + relay less on libevdev
Signed-off-by: Mattias Andrée <maandree@kth.se>
-rw-r--r--TODO2
-rw-r--r--common.h16
-rw-r--r--config.mk2
-rw-r--r--libgamepad.h161
-rw-r--r--libgamepad_close_device.c8
-rw-r--r--libgamepad_get_absolute_axis_info.c4
-rw-r--r--libgamepad_get_button_is_pressed.c11
-rw-r--r--libgamepad_open_device.c289
-rw-r--r--test-details.c2
9 files changed, 412 insertions, 83 deletions
diff --git a/TODO b/TODO
index 8cd3c2a..c82bd57 100644
--- a/TODO
+++ b/TODO
@@ -3,7 +3,7 @@ Add support for profiles (conformation, button names/icons/colours, controller l
Add support for quirks
Add support for leds
Add support for accelerometer and gyroscope
-Add support for creating instance from a file descriptor
Add man pages
Add readme file
Add force feedback tests
+Fix error details in .h documentation
diff --git a/common.h b/common.h
index aec4277..9889858 100644
--- a/common.h
+++ b/common.h
@@ -18,10 +18,24 @@
#include <unistd.h>
#include <libevdev/libevdev.h>
+#include <libsha2.h>
#include <libudev.h>
+#if defined(__GNUC__)
+# define LIBGAMEPAD_CONST__ __attribute__((__const__))
+#else
+# define LIBGAMEPAD_CONST__
+#endif
+
+
+#define READ_(ARR, I) ((ARR)[(I) / BITSOF(*(ARR))])
+#define SHIFT_(ARR, I) ((I) % BITSOF(*(ARR)))
+
#define ELEMSOF(A) (sizeof(A) / sizeof(*(A)))
+#define BITSOF(T) (8 * sizeof(T))
+#define SETBIT(ARR, I) (READ_(ARR, I) |= 1 << SHIFT_(ARR, I))
+#define GETBIT(ARR, I) ((READ_(ARR, I) >> SHIFT_(ARR, I)) & 1)
struct libgamepad_attachment_monitor {
@@ -34,6 +48,8 @@ struct libgamepad_device_internals {
int close_fd;
int require_sync;
struct libevdev *dev;
+ struct input_absinfo *absinfo;
+ uint8_t *buttons;
};
diff --git a/config.mk b/config.mk
index 8453750..7e9fb7c 100644
--- a/config.mk
+++ b/config.mk
@@ -9,6 +9,6 @@ CFLAGS = -Wall -g
LDFLAGS =
LIBS_CFLAGS = $$(pkg-config --cflags libevdev)
-LIBS_LDFLAGS = $$(pkg-config --libs libevdev) -ludev -lm
+LIBS_LDFLAGS = $$(pkg-config --libs libevdev) -ludev -lm -lsha2
INPUT_EVENT_CODES_H = /usr/include/linux/input-event-codes.h
diff --git a/libgamepad.h b/libgamepad.h
index b0efec4..e7abd6a 100644
--- a/libgamepad.h
+++ b/libgamepad.h
@@ -15,6 +15,13 @@
#include <libevdev/libevdev.h>
+#if defined(__GNUC__)
+# define LIBGAMEPAD_PURE__ __attribute__((__pure__))
+#else
+# define LIBGAMEPAD_PURE__
+#endif
+
+
/**
* Opaque structure for monitoring device attachment changes
*/
@@ -265,7 +272,6 @@ enum libgamepad_game_controller {
* Digital button "R" at upper right index finger position
* Digital button "ZL" at lower left index finger position
* Digital button "ZR" at lower right index finger position
- * Digital Stadia (icon: brand) button between analogue sticks
* 2 digital buttons at inner left thumb position:
* - North = "−"
* - East = Capture (icon: circle; shape: square)
@@ -491,7 +497,30 @@ enum libgamepad_game_controller {
*
* Released 2012-11-18 for Wii U
*
- * TODO Document Wii U Pro Controller
+ * Layout:
+ * Clickable analogue stick at upper left thumb position
+ * Clickable analogue stick at upper right thumb position
+ * Digital D-pad at lower left thumb position
+ * 4 digital buttons at lower right thumb position:
+ * - North = "X"
+ * - West = "Y"
+ * - South = "B"
+ * - East = "A"
+ * Digital button "L" at upper left index finger position
+ * Digital button "R" at upper right index finger position
+ * Digital button "ZL" at lower left index finger position
+ * Digital button "ZR" at lower right index finger position
+ * 3 digital buttons at centre thumb position:
+ * - West = "SELECT" (icon: minus symbol)
+ * - Centre = "HOME" (icon: house)
+ * - East = "START" (icon: plus symbol)
+ * Digital "POWER" (icon: power symbol with the 1 over top
+ * of the 0, colour: red) button at lower center thumb position
+ *
+ * Other features:
+ * - Wireless via Bluetooth
+ * - 4 digital LEDs numbered 1 through 4 (??)
+ * - 1 digital LEDs labelled "BATTERY" (??)
*/
LIBGAMEPAD_CONTROLLER_WII_U_PRO,
@@ -525,9 +554,25 @@ enum libgamepad_game_controller {
/**
* Classic Controller Pro
*
- * Released 2009-??-?? for Wii
+ * Released 2009-08-01 for Wii
*
- * TODO Document Classic Controller Pro
+ * Layout:
+ * Digital D-pad at upper left thumb position
+ * Analogue stick at lower left thumb position
+ * Analogue stick at lower right thumb position
+ * 4 digital buttons at upper right thumb position:
+ * - North = "x"
+ * - West = "y"
+ * - South = "b"
+ * - East = "a"
+ * Digital button "L" at upper left index finger position
+ * Digital button "R" at upper right index finger position
+ * Digital button "ZL" at lower left index finger position
+ * Digital button "ZR" at lower right index finger position
+ * 3 digital buttons at centre thumb position:
+ * - West = "SELECT" (icon: minus symbol)
+ * - Centre = "HOME" (icon: house, colour: blue)
+ * - East = "START" (icon: plus symbol)
*/
LIBGAMEPAD_CONTROLLER_WII_CLASSIC_PRO,
@@ -546,7 +591,23 @@ enum libgamepad_game_controller {
*
* Released 2006-11-19 for Wii
*
- * TODO Document Classic Controller
+ * Layout:
+ * Digital D-pad at upper left thumb position
+ * Analogue stick at lower left thumb position
+ * Analogue stick at lower right thumb position
+ * 4 digital buttons at upper right thumb position:
+ * - North = "x"
+ * - West = "y"
+ * - South = "b"
+ * - East = "a"
+ * Analogue button "L" at left index finger position
+ * Analogue button "R" at right index finger position
+ * Digital button "ZL" inward from left index finger position
+ * Digital button "ZR" inward from right index finger position
+ * 3 digital buttons at centre thumb position:
+ * - West = "SELECT" (icon: minus symbol)
+ * - Centre = "HOME" (icon: house, colour: blue)
+ * - East = "START" (icon: plus symbol)
*/
LIBGAMEPAD_CONTROLLER_WII_CLASSIC,
@@ -555,7 +616,14 @@ enum libgamepad_game_controller {
*
* Released 2006-11-19 for Wii
*
- * TODO Document Nunchuk
+ * Layout:
+ * Designed to be held in the user's dominant hand
+ * Analogue stick at thumb position
+ * Digital button "C" at upper index finger position
+ * Digital button "Z" at lower index finger position
+ *
+ * Other features:
+ * - Accelerometer
*/
LIBGAMEPAD_CONTROLLER_NUNCHUK,
@@ -574,6 +642,9 @@ enum libgamepad_game_controller {
* Released 2010-10-28 for Wii
*
* Adds (3 axis?) gyroscope to `LIBGAMEPAD_CONTROLLER_WII_REMOTE`
+ *
+ * Conforming controllers:
+ * - Wii Remote with Wii MotionPlus adapter
*/
LIBGAMEPAD_CONTROLLER_WII_REMOTE_PLUS,
@@ -763,7 +834,8 @@ enum libgamepad_game_controller {
*
* Released 1997-11-20 for PlayStation
*
- * TODO Document DualShock Analog Controller
+ * Adds rubble support to `LIBGAMEPAD_CONTROLLER_DUAL_ANALOG`
+ * (TODO What does digital mode on DualShock do?)
*/
LIBGAMEPAD_CONTROLLER_DUAL_SHOCK,
@@ -771,8 +843,32 @@ enum libgamepad_game_controller {
* Dual Analog Controller
*
* Released 1997-04-25 for PlayStation
+ *
+ * The first Japanese version of this controller
+ * had rumble support and is therefore better
+ * reported as `LIBGAMEPAD_CONTROLLER_DUAL_SHOCK`
+ *
+ * Layout:
+ * Digital D-pad at upper left thumb position
+ * Clickable analogue stick at lower left thumb position
+ * Clickable analogue stick at lower right thumb position
+ * 4 digital buttons at upper right thumb position
+ * - North = Triangle (colour: green)
+ * - West = Square (colour: pink)
+ * - South = Cross (colour: blue)
+ * - East = Circle (colour: red)
+ * Digital button "L1" at upper left index finger position
+ * Digital button "R1" at upper right index finger position
+ * Digital button "L2" at lower left index finger position
+ * Digital button "R2" at lower right index finger position
+ * 2 digital buttons at centre position:
+ * - West = "SELECT" (shape: flat rectangle)
+ * - East = "START" (shape: flat play icon)
*
- * TODO Document Dual Analog Controller
+ * Other features:
+ * - LED-indicated digital "ANALOG" button at south part of center position
+ * for toggling between analogue and digital mode
+ * (TODO What does digital mode on Dual Analog do?)
*/
LIBGAMEPAD_CONTROLLER_DUAL_ANALOG,
@@ -885,7 +981,7 @@ enum libgamepad_game_controller {
* Digital "MODE" button right index finger position
* Digital "START" (shape: horizontal line) button at centre position
*
- * Conforming platforms:
+ * Conforming controllers:
* - Sega Nomad (however "MODE" is east of "START" which is far south east of "A")
*/
LIBGAMEPAD_CONTROLLER_6_BUTTON_ARCADE_PAD,
@@ -973,7 +1069,7 @@ enum libgamepad_game_controller {
* - West = "SELECT"
* - East = "START"
*
- * Conforming platforms:
+ * Conforming controllers:
* - Nintendo Game Boy
* - Supervision (however "START" is also labelled "PAUSE" and the "SELECT" and "START"
* buttons are horizontal and north (slightly east) for "A" and "B" which
@@ -1040,7 +1136,7 @@ enum libgamepad_game_controller {
* Digital "L" button at left index finger position
* Digital "R" button at right index finger position
*
- * Conforming platforms:
+ * Conforming controllers:
* - Game King (however "SELECT" and "START" are north of D-pad,
* "SELECT" to east and "START to west)
*/
@@ -1100,7 +1196,10 @@ enum libgamepad_game_controller {
* BTN_EAST if there are 2 action buttons but reversed if there are 3
* action buttons: (from bottom up) BTN_EAST, BTN_SOUTH, BTN_WEST
* (`LIBGAMEPAD_CONTROLLER_LINUX_4_12_RECTIFIED_3BTN` is added to deal
- * with this problem).
+ * with this problem). In the event that a controller has only one
+ * action button (and truly is a gamepad and not a joystick), it shall
+ * be reported as BTN_SOUTH as this one is designated as mandatory
+ * (BTN_GAMEPAD) is an alias of it.
*/
LIBGAMEPAD_CONTROLLER_LINUX_4_12,
@@ -1383,18 +1482,18 @@ struct libgamepad_device {
/**
* Human-readable device (sub- or superdevice) name
*/
- const char *name;
+ char *name;
/**
* ID that is supposted to be unique to the device
* (sub- or superdevice)
*/
- const char *unique_id;
+ char *unique_id;
/**
* The location if the device
*/
- const char *physical_location;
+ char *physical_location;
/**
* Number of (digital) buttons/keys present
@@ -1452,6 +1551,18 @@ struct libgamepad_device {
* Bitmap of supported force feedback effects
*/
uint8_t force_feedback_support[(FF_CNT + 7) / 8];
+
+ /**
+ * Device fingerprint that does not take `.unique_id`
+ * into account
+ */
+ char fingerprint[65];
+
+ /**
+ * Device fingerprint that does take `.unique_id`
+ * into account
+ */
+ char fingerprint_unique[65];
};
@@ -1697,6 +1808,15 @@ int16_t libgamepad_get_relative_axis_by_name(const char *);
/**
* Get whether a button/key is pressed down or not
*
+ * libgamepad caches the last read button/key state,
+ * and will return the cached .state The underlaying
+ * ioctl(3) will has the ability to read a state
+ * even when the device is grabbed, but even if the
+ * device is not grabbed, the state may be out of
+ * that. Because the state may be out of date,
+ * libgamepad reads the current state when a device
+ * is opened.
+ *
* @param device The device to retrieve the information for
* @param code The button/key
* @return 1 if the button/key is pressed down,
@@ -1707,11 +1827,20 @@ int libgamepad_get_button_is_pressed(struct libgamepad_device *, uint16_t);
/**
* Get information about an absolute axis
*
+ * libgamepad caches the last read axis value, and
+ * will return the cached value. The underlaying
+ * ioctl(3) will has the ability to read a value
+ * even when the device is grabbed, but even if the
+ * device is not grabbed, the value may be out of
+ * that. Because the value may be out of date,
+ * libgamepad reads the current state when a device
+ * is opened.
+ *
* @param device The device to retrieve the information for
* @param code The axis
* @return Information about the axis
*/
-const struct input_absinfo *libgamepad_get_absolute_axis_info(struct libgamepad_device *, uint16_t);
+LIBGAMEPAD_PURE__ const struct input_absinfo *libgamepad_get_absolute_axis_info(struct libgamepad_device *, uint16_t);
/* `struct input_absinfo` is defined in <linux/input.h> */
diff --git a/libgamepad_close_device.c b/libgamepad_close_device.c
index 3b00149..273c7f8 100644
--- a/libgamepad_close_device.c
+++ b/libgamepad_close_device.c
@@ -11,14 +11,22 @@ libgamepad_close_device(struct libgamepad_device *device)
close(device->fd);
if (device->internals->dev)
libevdev_free(device->internals->dev);
+ free(device->internals->absinfo);
+ free(device->internals->buttons);
free(device->internals);
device->internals = NULL;
}
free(device->buttons);
free(device->absolute_axes);
free(device->relative_axes);
+ free(device->name);
+ free(device->unique_id);
+ free(device->physical_location);
device->buttons = NULL;
device->absolute_axes = NULL;
device->relative_axes = NULL;
+ device->name = NULL;
+ device->unique_id = NULL;
+ device->physical_location = NULL;
}
}
diff --git a/libgamepad_get_absolute_axis_info.c b/libgamepad_get_absolute_axis_info.c
index a5aa4d5..ebd38fd 100644
--- a/libgamepad_get_absolute_axis_info.c
+++ b/libgamepad_get_absolute_axis_info.c
@@ -5,5 +5,7 @@
const struct input_absinfo *
libgamepad_get_absolute_axis_info(struct libgamepad_device *device, uint16_t code)
{
- return libevdev_get_abs_info(device->internals->dev, (unsigned int)code);
+ if ((size_t)code >= ELEMSOF(device->absolute_axis_map) || device->absolute_axis_map[code] == -1)
+ return NULL;
+ return &device->internals->absinfo[device->absolute_axis_map[code]];
}
diff --git a/libgamepad_get_button_is_pressed.c b/libgamepad_get_button_is_pressed.c
index e3c0265..3c545c1 100644
--- a/libgamepad_get_button_is_pressed.c
+++ b/libgamepad_get_button_is_pressed.c
@@ -2,8 +2,17 @@
#include "common.h"
+#if defined(__GNUC__)
+# pragma GCC diagnostic ignored "-Wsuggest-attribute=pure"
+#endif
+
+
int
libgamepad_get_button_is_pressed(struct libgamepad_device *device, uint16_t code)
{
- return libevdev_get_event_value(device->internals->dev, EV_KEY, (unsigned int)code);
+ int16_t i;
+ if ((size_t)code >= ELEMSOF(device->button_map))
+ return 0;
+ i = device->button_map[code];
+ return i >= 0 && (int)GETBIT(device->internals->buttons, (uint16_t)i);
}
diff --git a/libgamepad_open_device.c b/libgamepad_open_device.c
index 3c2a751..22163a4 100644
--- a/libgamepad_open_device.c
+++ b/libgamepad_open_device.c
@@ -1,39 +1,165 @@
/* See LICENSE file for copyright and license details. */
#include "common.h"
+#define ELEMS_REQUIRED(BITS, TYPE) ((BITS + BITSOF(TYPE) - 1) / BITSOF(TYPE))
+#define MAX2(A, B) ((A) > (B) ? (A) : (B))
+#define MAX4(A, B, C, D) (MAX2(MAX2(A, B), MAX2(C, D)))
+
+
+static void
+store_uint(uint64_t value, char *buf)
+{
+ buf[0] = (char)((value >> 0) & 255);
+ buf[1] = (char)((value >> 8) & 255);
+ buf[2] = (char)((value >> 16) & 255);
+ buf[3] = (char)((value >> 24) & 255);
+ buf[4] = (char)((value >> 32) & 255);
+ buf[5] = (char)((value >> 40) & 255);
+ buf[6] = (char)((value >> 48) & 255);
+ buf[7] = (char)((value >> 56) & 255);
+}
+
+
+LIBGAMEPAD_CONST__
+static size_t
+popcount(uint8_t value)
+{
+ size_t n = 0;
+ for (; value; value >>= 1)
+ if (value & 1)
+ n += 1;
+ return n;
+}
+
+
+static int
+get_fingerprint(struct libgamepad_device *device, char non_unique[65], char unique[65])
+{
+ struct libsha2_state s, s2;
+ char buf[128];
+ size_t i, n;
+
+ if (libsha2_init(&s, LIBSHA2_256))
+ return -1;
+
+ libsha2_update(&s, device->name, 8 * strlen(device->name) + 8);
+ libsha2_update(&s, device->physical_location, 8 * strlen(device->physical_location) + 8);
+
+ store_uint(device->vendor, &buf[0]);
+ store_uint(device->product, &buf[8]);
+ store_uint(device->version, &buf[16]);
+ libsha2_update(&s, buf, 3 * 64);
+
+ store_uint(device->nbuttons, buf);
+ for (n = 8, i = 0; i < ELEMSOF(device->button_map); i++) {
+ if (device->button_map[i] >= 0) {
+ store_uint((uint64_t)i, &buf[n]);
+ n += 8;
+ if (n == sizeof(buf)) {
+ libsha2_update(&s, buf, 8 * n);
+ n = 0;
+ }
+ }
+ }
+ libsha2_update(&s, buf, 8 * n);
+ n = 0;
+
+ store_uint(device->nabsolute_axes, buf);
+ for (n = 8, i = 0; i < ELEMSOF(device->absolute_axis_map); i++) {
+ if (device->absolute_axis_map[i] >= 0) {
+ store_uint((uint64_t)i, &buf[n]);
+ n += 8;
+ if (n == sizeof(buf)) {
+ libsha2_update(&s, buf, 8 * n);
+ n = 0;
+ }
+ }
+ }
+ libsha2_update(&s, buf, 8 * n);
+ n = 0;
+
+ store_uint(device->nrelative_axes, buf);
+ for (n = 8, i = 0; i < ELEMSOF(device->relative_axis_map); i++) {
+ if (device->relative_axis_map[i] >= 0) {
+ store_uint((uint64_t)i, &buf[n]);
+ n += 8;
+ if (n == sizeof(buf)) {
+ libsha2_update(&s, buf, 8 * n);
+ n = 0;
+ }
+ }
+ }
+ libsha2_update(&s, buf, 8 * n);
+ n = 0;
+
+ n = 0;
+ for (i = 0; i < ELEMSOF(device->force_feedback_support); i++)
+ if (device->force_feedback_support[i])
+ n += popcount(device->force_feedback_support[i]);
+ store_uint((uint64_t)n, buf);
+ for (n = 8, i = 0; i < BITSOF(device->force_feedback_support); i++) {
+ if (GETBIT(device->force_feedback_support, i)) {
+ store_uint((uint64_t)i, &buf[n]);
+ n += 8;
+ if (n == sizeof(buf)) {
+ libsha2_update(&s, buf, 8 * n);
+ n = 0;
+ }
+ }
+ }
+ libsha2_update(&s, buf, 8 * n);
+ n = 0;
+
+ memcpy(&s2, &s, sizeof(s));
+ libsha2_update(&s2, device->unique_id, 8 * strlen(device->unique_id) + 8);
+
+ libsha2_digest(&s, NULL, 0, buf);
+ libsha2_behex_lower(non_unique, buf, 32);
+ libsha2_digest(&s2, NULL, 0, buf);
+ libsha2_behex_lower(unique, buf, 32);
+ return 0;
+}
+
int
libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char *path, int mode)
{
- int err;
- unsigned int i;
+ unsigned long int bits[ELEMS_REQUIRED(MAX4(ELEMSOF(devicep->button_map),
+ ELEMSOF(devicep->absolute_axis_map),
+ ELEMSOF(devicep->relative_axis_map),
+ ELEMSOF(devicep->force_feedback_support)), long int)];
+ int err, r;
+ int16_t j;
uint16_t n;
+ unsigned int i, max;
struct input_id id;
+ char *buf = NULL;
+ size_t bufsize = 0;
+ void *new;
+ memset(devicep, 0, sizeof(*devicep));
devicep->fd = dirfd;
- devicep->internals = NULL;
- devicep->buttons = NULL;
- devicep->absolute_axes = NULL;
- devicep->relative_axes = NULL;
- memset(devicep->force_feedback_support, 0, sizeof(devicep->force_feedback_support));
devicep->internals = calloc(1, sizeof(*devicep->internals));
if (!devicep->internals)
return -1;
+
if (path && *path) {
devicep->fd = openat(dirfd, path, mode);
- if (devicep->fd < 0) {
- libgamepad_close_device(devicep);
- return -1;
- }
+ if (devicep->fd < 0)
+ goto fail;
devicep->internals->close_fd = 1;
}
- if (ioctl(devicep->fd, EVIOCGID, &id)) {
- libgamepad_close_device(devicep);
- return -1;
- }
+
+ if (ioctl(devicep->fd, EVIOCGID, &id) < 0)
+ goto fail;
+ devicep->bus_type = (unsigned int)id.bustype;
+ devicep->vendor = (unsigned int)id.vendor;
+ devicep->product = (unsigned int)id.product;
+ devicep->version = (unsigned int)id.version;
+
err = libevdev_new_from_fd(devicep->fd, &devicep->internals->dev);
if (err < 0) {
@@ -42,68 +168,105 @@ libgamepad_open_device(struct libgamepad_device *devicep, int dirfd, const char
return -1;
}
- devicep->name = libevdev_get_name(devicep->internals->dev);
- devicep->unique_id = libevdev_get_uniq(devicep->internals->dev);
- devicep->physical_location = libevdev_get_phys(devicep->internals->dev);
- devicep->bus_type = (unsigned int)id.bustype;
- devicep->vendor = (unsigned int)id.vendor;
- devicep->product = (unsigned int)id.product;
- devicep->version = (unsigned int)id.version;
-
- devicep->nbuttons = 0;
- for (i = 0; i < KEY_CNT; i++) {
- devicep->button_map[i] = -1;
- if (libevdev_has_event_code(devicep->internals->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->internals->dev, EV_ABS, i))
- devicep->absolute_axis_map[i] = (int16_t)devicep->nabsolute_axes++;
- }
+#define GET_STRING(EVIOCMACRO, OUTPUT)\
+ do {\
+ for (;;) {\
+ r = ioctl(devicep->fd, EVIOCMACRO(bufsize), buf);\
+ if (r < 0)\
+ goto fail_free_buf;\
+ if ((size_t)r < bufsize)\
+ break;\
+ new = realloc(buf, bufsize += 32);\
+ if (!new)\
+ goto fail_free_buf;\
+ buf = new;\
+ }\
+ buf[r] = '\0';\
+ *OUTPUT = strdup(buf);\
+ if (!*OUTPUT)\
+ goto fail_free_buf;\
+ } while (0)
+ GET_STRING(EVIOCGNAME, &devicep->name);
+ GET_STRING(EVIOCGUNIQ, &devicep->unique_id);
+ GET_STRING(EVIOCGPHYS, &devicep->physical_location);
+ free(buf);
- devicep->nrelative_axes = 0;
- for (i = 0; i < REL_CNT; i++) {
- devicep->relative_axis_map[i] = -1;
- if (libevdev_has_event_code(devicep->internals->dev, EV_REL, i))
- devicep->relative_axis_map[i] = (int16_t)devicep->nrelative_axes++;
- }
- for (i = 0; i < FF_CNT; i++)
- if (libevdev_has_event_code(devicep->internals->dev, EV_FF, i))
- devicep->force_feedback_support[i / 8] |= (uint8_t)(1 << (i & 7));
+#define CREATE_MAPS(EVENTS, SINGULAR, PLURAL)\
+ do {\
+ /* Code-to-index map */\
+ r = ioctl(devicep->fd, EVIOCGBIT(EVENTS, sizeof(bits)), bits);\
+ if (r < 0)\
+ goto fail;\
+ max = (unsigned int)r * 8;\
+ devicep->n##PLURAL = 0;\
+ for (i = 0; i < ELEMSOF(devicep->SINGULAR##_map); i++) {\
+ devicep->SINGULAR##_map[i] = -1;\
+ if (i < max && ((bits[i / BITSOF(*bits)] >> (i % BITSOF(*bits))) & 1))\
+ devicep->SINGULAR##_map[i] = (int16_t)devicep->n##PLURAL++;\
+ }\
+ \
+ /* Index-to-code map */\
+ if (devicep->n##PLURAL) {\
+ devicep->PLURAL = calloc(devicep->n##PLURAL, sizeof(*devicep->PLURAL));\
+ if (!devicep->PLURAL)\
+ goto fail;\
+ for (i = 0, n = 0; i < ELEMSOF(devicep->SINGULAR##_map); i++)\
+ if (devicep->SINGULAR##_map[i] >= 0)\
+ devicep->PLURAL[n++] = (uint16_t)i;\
+ }\
+ } while (0)
+
+ CREATE_MAPS(EV_KEY, button, buttons);
+ CREATE_MAPS(EV_ABS, absolute_axis, absolute_axes);
+ CREATE_MAPS(EV_REL, relative_axis, relative_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)
+ devicep->internals->absinfo = calloc(devicep->nabsolute_axes, sizeof(*devicep->internals->absinfo));
+ if (!devicep->internals->absinfo)
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;
}
+ for (i = 0; i < devicep->nabsolute_axes; i++)
+ if (ioctl(devicep->fd, EVIOCGABS((unsigned int)devicep->absolute_axes[i]), &devicep->internals->absinfo[i]) < 0)
+ goto fail;
+
- if (devicep->nrelative_axes) {
- devicep->relative_axes = calloc(devicep->nrelative_axes, sizeof(*devicep->relative_axes));
- if (!devicep->relative_axes)
+ if (devicep->nbuttons) {
+ devicep->internals->buttons = calloc((devicep->nbuttons + 7) / 8, sizeof(*devicep->internals->buttons));
+ if (!devicep->internals->buttons)
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;
+ r = ioctl(devicep->fd, EVIOCGKEY(sizeof(bits)), bits);
+ if (r < 0)
+ goto fail;
+ max = (unsigned int)r * 8;
+ for (i = 0; i < max && i < ELEMSOF(devicep->button_map); i++) {
+ j = devicep->button_map[i];
+ if (j >= 0 && GETBIT(bits, i))
+ SETBIT(devicep->internals->buttons, (uint16_t)j);
+ }
}
+
+ r = ioctl(devicep->fd, EVIOCGBIT(EV_FF, sizeof(bits)), bits);
+ if (r < 0)
+ goto fail;
+ max = (unsigned int)r * 8;
+ for (i = 0; i < BITSOF(devicep->force_feedback_support); i++)
+ if (i < max && ((bits[i / BITSOF(*bits)] >> (i % BITSOF(*bits))) & 1))
+ SETBIT(devicep->force_feedback_support, i);
+
+
+ if (get_fingerprint(devicep, devicep->fingerprint, devicep->fingerprint_unique))
+ goto fail;
+
+ /* TODO get actual KEY/ABS state */
+
return 0;
+fail_free_buf:
+ free(buf);
fail:
libgamepad_close_device(devicep);
return -1;
diff --git a/test-details.c b/test-details.c
index e834819..f8b2365 100644
--- a/test-details.c
+++ b/test-details.c
@@ -31,6 +31,8 @@ main(int argc, char *argv[])
printf("Name: %s\n", gamepad.name);
printf("Unique id: %s\n", gamepad.unique_id);
printf("Physical location: %s\n", gamepad.physical_location);
+ printf("Fingerprint: %s\n", gamepad.fingerprint);
+ printf("Unique fingerprint: %s\n", gamepad.fingerprint_unique);
printf("Buttons:\n");
for (i = 0; i < gamepad.nbuttons; i++) {