X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=extras%2Finput_id%2Finput_id.c;h=6df9ae423a65301376f6573c0bd876bd55a9aa4e;hb=1d8296d6ed0274d70b576e9dbcb22496a87e9161;hp=393a05d53a0e8879e0f148125aaead1de965b392;hpb=a6cf7734015dfc4479f4fdd4585d8953979fe0b0;p=elogind.git diff --git a/extras/input_id/input_id.c b/extras/input_id/input_id.c index 393a05d53..6df9ae423 100644 --- a/extras/input_id/input_id.c +++ b/extras/input_id/input_id.c @@ -22,9 +22,14 @@ #include #include #include +#include #include +#include #include +#include "libudev.h" +#include "libudev-private.h" + /* we must use this kernel-compatible implementation */ #define BITS_PER_LONG (sizeof(unsigned long) * 8) #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) @@ -33,55 +38,85 @@ #define LONG(x) ((x)/BITS_PER_LONG) #define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) +static int debug = 0; + +static void log_fn(struct udev *udev, int priority, + const char *file, int line, const char *fn, + const char *format, va_list args) +{ + if (debug) { + fprintf(stderr, "%s: ", fn); + vfprintf(stderr, format, args); + } else { + vsyslog(priority, format, args); + } +} + /* - * Read a capabilities/name file and return bitmask. - * @param path File to open - * @param bitmask: Output array; must have max_size elements + * Read a capability attribute and return bitmask. + * @param dev udev_device + * @param attr sysfs attribute name (e. g. "capabilities/key") + * @param bitmask: Output array which has a sizeof of bitmask_size */ -static void get_cap_mask (const char *path, unsigned long *bitmask, size_t max_size) +static void get_cap_mask (struct udev_device *dev, const char* attr, + unsigned long *bitmask, size_t bitmask_size) { - FILE* f; - int i; char text[4096]; + unsigned i; char* word; unsigned long val; - f = fopen(path, "r"); - if (f == NULL) { - perror("opening caps file"); - exit(1); - } - if (fgets(text, sizeof(text), f) == NULL) { - perror("fgets"); - exit(1); - } - fclose(f); - - memset (bitmask, 0, max_size); + snprintf(text, sizeof(text), "%s", udev_device_get_sysattr_value(dev, attr)); + + info(udev_device_get_udev(dev), "%s raw kernel attribute: %s\n", attr, text); + + memset (bitmask, 0, bitmask_size); i = 0; - while ((word = strrchr(text, ' ')) != NULL) { - val = strtoul (word+1, NULL, 16); - bitmask[i] = val; + while ((word = strrchr(text, ' ')) != NULL) { + val = strtoul (word+1, NULL, 16); + if (i < bitmask_size/sizeof(unsigned long)) + bitmask[i] = val; + else + info(udev_device_get_udev(dev), "Ignoring %s block %lX which is larger than maximum size\n", attr, val); *word = '\0'; ++i; - } + } val = strtoul (text, NULL, 16); - bitmask[i] = val; + if (i < bitmask_size/sizeof(unsigned long)) + bitmask[i] = val; + else + info(udev_device_get_udev(dev), "Ignoring %s block %lX which is larger than maximum size\n", attr, val); + + if (debug) { + /* printf pattern with the right unsigned long number of hex chars */ + snprintf(text, sizeof(text), " bit %%4u: %%0%zilX\n", 2*sizeof(unsigned long)); + info(udev_device_get_udev(dev), "%s decoded bit map:\n", attr); + val = bitmask_size/sizeof (unsigned long); + /* skip over leading zeros */ + while (bitmask[val-1] == 0 && val > 0) + --val; + for (i = 0; i < val; ++i) + info(udev_device_get_udev(dev), text, i * BITS_PER_LONG, bitmask[i]); + } } /* pointer devices */ -static void test_pointers (const unsigned long* bitmask_abs, const unsigned long* bitmask_key, const unsigned long* bitmask_rel) +static void test_pointers (const unsigned long* bitmask_ev, + const unsigned long* bitmask_abs, + const unsigned long* bitmask_key, + const unsigned long* bitmask_rel) { int is_mouse = 0; int is_touchpad = 0; - if (test_bit (ABS_PRESSURE, bitmask_abs)) - is_touchpad = 1; + if (!test_bit (EV_KEY, bitmask_ev)) + return; - if (test_bit (ABS_X, bitmask_abs) && test_bit (ABS_Y, bitmask_abs)) { - if (test_bit (BTN_STYLUS, bitmask_key)) + if (test_bit (EV_ABS, bitmask_ev) && + test_bit (ABS_X, bitmask_abs) && test_bit (ABS_Y, bitmask_abs)) { + if (test_bit (BTN_STYLUS, bitmask_key) || test_bit (BTN_TOOL_PEN, bitmask_key)) puts("ID_INPUT_TABLET=1"); - else if (test_bit (BTN_TOUCH, bitmask_key)) + else if (test_bit (BTN_TOOL_FINGER, bitmask_key) && !test_bit (BTN_TOOL_PEN, bitmask_key)) is_touchpad = 1; else if (test_bit (BTN_TRIGGER, bitmask_key) || test_bit (BTN_A, bitmask_key) || @@ -91,9 +126,13 @@ static void test_pointers (const unsigned long* bitmask_abs, const unsigned long /* This path is taken by VMware's USB mouse, which has * absolute axes, but no touch/pressure button. */ is_mouse = 1; + else if (test_bit (BTN_TOUCH, bitmask_key)) + puts("ID_INPUT_TOUCHSCREEN=1"); } - if (test_bit (REL_X, bitmask_rel) && test_bit (REL_Y, bitmask_rel)) + if (test_bit (EV_REL, bitmask_ev) && + test_bit (REL_X, bitmask_rel) && test_bit (REL_Y, bitmask_rel) && + test_bit (BTN_MOUSE, bitmask_key)) is_mouse = 1; if (is_mouse) @@ -103,17 +142,38 @@ static void test_pointers (const unsigned long* bitmask_abs, const unsigned long } /* key like devices */ -static void test_key (const unsigned long* bitmask_key) +static void test_key (struct udev *udev, + const unsigned long* bitmask_ev, + const unsigned long* bitmask_key) { unsigned i; - unsigned long acc; + unsigned long found; unsigned long mask; /* do we have any KEY_* capability? */ - acc = 0; - for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) - acc |= bitmask_key[i]; - if (acc > 0) + if (!test_bit (EV_KEY, bitmask_ev)) { + info(udev, "test_key: no EV_KEY capability\n"); + return; + } + + /* only consider KEY_* here, not BTN_* */ + found = 0; + for (i = 0; i < BTN_MISC/BITS_PER_LONG; ++i) { + found |= bitmask_key[i]; + info(udev, "test_key: checking bit block %lu for any keys; found=%i\n", i*BITS_PER_LONG, found > 0); + } + /* If there are no keys in the lower block, check the higher block */ + if (!found) { + for (i = KEY_OK; i < BTN_TRIGGER_HAPPY; ++i) { + if (test_bit (i, bitmask_key)) { + info(udev, "test_key: Found key %x in high block\n", i); + found = 1; + break; + } + } + } + + if (found > 0) puts("ID_INPUT_KEY=1"); /* the first 32 bits are ESC, numbers, and Q to D; if we have all of @@ -123,32 +183,93 @@ static void test_key (const unsigned long* bitmask_key) puts("ID_INPUT_KEYBOARD=1"); } +static void help () +{ + printf("Usage: input_id [options] \n" + " --debug debug to stderr\n" + " --help print this help text\n\n"); +} + int main (int argc, char** argv) { - char capfile[PATH_MAX]; + struct udev *udev; + struct udev_device *dev; + + static const struct option options[] = { + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + {} + }; + + char devpath[PATH_MAX]; + unsigned long bitmask_ev[NBITS(EV_MAX)]; unsigned long bitmask_abs[NBITS(ABS_MAX)]; unsigned long bitmask_key[NBITS(KEY_MAX)]; - unsigned long bitmask_rel[NBITS(REL_MAX)]; + unsigned long bitmask_rel[NBITS(REL_MAX)]; + + udev = udev_new(); + if (udev == NULL) + return 1; - if (argc != 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); + udev_log_init("input_id"); + udev_set_log_fn(udev, log_fn); + + /* CLI argument parsing */ + while (1) { + int option; + + option = getopt_long(argc, argv, "dxh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'd': + debug = 1; + if (udev_get_log_priority(udev) < LOG_INFO) + udev_set_log_priority(udev, LOG_INFO); + break; + case 'h': + help(); + exit(0); + default: + exit(1); + } + } + + if (argv[optind] == NULL) { + help(); exit(1); } + /* get the device */ + snprintf(devpath, sizeof(devpath), "%s/%s", udev_get_sys_path(udev), argv[optind]); + dev = udev_device_new_from_syspath(udev, devpath); + if (dev == NULL) { + fprintf(stderr, "unable to access '%s'\n", devpath); + return 1; + } + + /* walk up the parental chain until we find the real input device; the + * argument is very likely a subdevice of this, like eventN */ + while (dev != NULL && udev_device_get_sysattr_value(dev, "capabilities/ev") == NULL) + dev = udev_device_get_parent_with_subsystem_devtype(dev, "input", NULL); + + /* not an "input" class device */ + if (dev == NULL) + return 0; + /* Use this as a flag that input devices were detected, so that this * program doesn't need to be called more than once per device */ puts("ID_INPUT=1"); - snprintf(capfile, sizeof(capfile), "/sys/%s/device/capabilities/abs", argv[1]); - get_cap_mask (capfile, bitmask_abs, sizeof (bitmask_abs)); - snprintf(capfile, sizeof(capfile), "/sys/%s/device/capabilities/rel", argv[1]); - get_cap_mask (capfile, bitmask_rel, sizeof (bitmask_rel)); - snprintf(capfile, sizeof(capfile), "/sys/%s/device/capabilities/key", argv[1]); - get_cap_mask (capfile, bitmask_key, sizeof (bitmask_key)); + get_cap_mask (dev, "capabilities/ev", bitmask_ev, sizeof (bitmask_ev)); + get_cap_mask (dev, "capabilities/abs", bitmask_abs, sizeof (bitmask_abs)); + get_cap_mask (dev, "capabilities/rel", bitmask_rel, sizeof (bitmask_rel)); + get_cap_mask (dev, "capabilities/key", bitmask_key, sizeof (bitmask_key)); - test_pointers(bitmask_abs, bitmask_key, bitmask_rel); + test_pointers(bitmask_ev, bitmask_abs, bitmask_key, bitmask_rel); - test_key(bitmask_key); + test_key(udev, bitmask_ev, bitmask_key); return 0; }