[PATCH libevdev 3/5] Add libevdev_event_type/code_from_name() resolvers

Peter Hutterer peter.hutterer at who-t.net
Sun Oct 6 18:17:36 PDT 2013


On Fri, Oct 04, 2013 at 08:37:33PM +0200, David Herrmann wrote:
> Two new helpers are added:
> (1) libevdev_event_type_from_name() takes a string describing an EV_*
> event type and returns the given event-type constant.
> (2) libevdev_event_code_from_name() takes a string describing an event
> code and returns the given event-code constant.
> 
> Both functions share common behavior:
>  - Return -1 if the string is not found.
>  - Accept a string-length argument (if <0 zero-terminated string is
>    assumed). This is highly useful for lexers. If you mmap() a file it is
>    usually read-only and a lexer cannot assume an identifier is
>    zero-terminated. So without an explicit size-argument, a lexer would
>    have to copy the string before passing it to the resolver.
>  - Accept a flags argument. Two flags exist: One flag to make the resolver
>    case-sensitive (case-insensitive is the default). The other flag allows

what's the use-case of having case-sensitive matching? these defines are always
uppercase and I doubt that'll change soon. the only reason for
case-sensitive matching I can see is if the caller doesn't quite know if
something is an event code or not and want's to decide based on the case
(e.g. "trigger" in a text isn't BTN_TRIGGER). and that's more likely to go
wrong than right anyway ("a" vs "A").

>    the caller to force the resolver to fail if the code prefix is omitted.
>    By default, the resolver allows omitting prefixes like "EV_", "KEY_"
>    and so on to simplify configuration files where this is obvious,
>    anyway. However, this might be ambigious or misleading in some
>    use-cases so this flag allows to disable the prefix-guessing.
> 
> libevdev_event_code_from_name() has one additional argument which
> specifies the type/group of the code. If it is -1, the resolver tries to
> deduce it from the string prefix. Otherwise, only the given group is
> searched.

I'm not a big fan of the API just yet, tbh. I'd set the DEFAULT flags enum
to set a specific value so that 0 is an invalid value.
and a define/enum for type unset would be better, because right now this is
a valid call:
   libevdev_event_code_from_name("A", -1, -1, 0);
and quite frankly, that's not easy to read. A better approach is something
like this:
   libevdev_event_code_from_name("A", -1, LIBEVDEV_GUESS_TYPE,
                                 LIBEVDEV_RESOLVER_FLAGS_DEFAULT);
still leaves the -1 for the size, but defining that to be some constant
seems over the top.

> Another special behavior is BTN_* parsing. The BTN_* constants may overlap
> with KEY_* constants and thus, if the prefix is omitted, the parser cannot
> know which to return. We explicitly prefer KEY_* codes in that case so
> users have to add the prefix explicitly if they want the BTN_* code of a
> conflicting EV_KEY code.

I don't think this behaviouri is a good idea, it's likely to produce the
wrong result when people don't check the exact behaviour and a lookup
failure for BTN_A is a lot easier to debug than a wrong code because
we returned KEY_A instead.

better not to allow it and require users to use BTN_ as prefix for button
lookups.
 
> Signed-off-by: David Herrmann <dh.herrmann at gmail.com>
> ---
>  libevdev/Makefile.am      |   1 +
>  libevdev/libevdev-names.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++
>  libevdev/libevdev.h       |  55 +++++++++++++
>  test/Makefile.am          |   1 +
>  4 files changed, 251 insertions(+)
>  create mode 100644 libevdev/libevdev-names.c
> 
> diff --git a/libevdev/Makefile.am b/libevdev/Makefile.am
> index 552df70..861be66 100644
> --- a/libevdev/Makefile.am
> +++ b/libevdev/Makefile.am
> @@ -9,6 +9,7 @@ libevdev_la_SOURCES = \
>                     libevdev-uinput.c \
>                     libevdev-uinput.h \
>                     libevdev-uinput-int.h \
> +                   libevdev-names.c \
>                     libevdev.c
>  
>  libevdev_la_LDFLAGS = \
> diff --git a/libevdev/libevdev-names.c b/libevdev/libevdev-names.c
> new file mode 100644
> index 0000000..ad63d64
> --- /dev/null
> +++ b/libevdev/libevdev-names.c
> @@ -0,0 +1,194 @@
> +/*
> + * Copyright © 2013 David Herrmann <dh.herrmann at gmail.com>
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and its
> + * documentation for any purpose is hereby granted without fee, provided that
> + * the above copyright notice appear in all copies and that both that copyright
> + * notice and this permission notice appear in supporting documentation, and
> + * that the name of the copyright holders not be used in advertising or
> + * publicity pertaining to distribution of the software without specific,
> + * written prior permission.  The copyright holders make no representations
> + * about the suitability of this software for any purpose.  It is provided "as
> + * is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, 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.
> + */
> +
> +#include <config.h>
> +#include <errno.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <strings.h>
> +
> +#include "libevdev.h"
> +#include "libevdev-int.h"
> +#include "libevdev-util.h"
> +#include "event-names.h"
> +
> +struct name_lookup {
> +	const char *name;
> +	size_t len;

this should be ssize_t if you're using -1 as value.

> +};
> +
> +static int cmp_entry_case(const void *vlookup, const void *ventry)
> +{
> +	const struct name_lookup *lookup = vlookup;
> +	const struct name_entry *entry = ventry;
> +	int r;
> +
> +	r = strncasecmp(lookup->name, entry->name, lookup->len);
> +	if (!r) {
> +		if (entry->name[lookup->len])
> +			r = -1;
> +		else
> +			r = 0;

r is already 0 here, this can be compressed.

> +	}
> +
> +	return r;
> +}
> +
> +static bool
> +fill_lookup(struct name_lookup *lookup, const char *name, ssize_t len,
> +	    const char *prefix, size_t plen, enum libevdev_resolver_flag flags)
> +{
> +	lookup->name = name;
> +	lookup->len = (len < 0) ? strlen(name) : (size_t)len;
> +
> +	if (startswith_case(lookup->name, lookup->len, prefix, plen)) {
> +		if (!(flags & LIBEVDEV_RESOLVER_FLAG_CASE_SENSITIVE) ||
> +		    startswith(lookup->name, lookup->len, prefix, plen)) {
> +			lookup->name = &lookup->name[plen];
> +			lookup->len -= plen;
> +			return true;
> +		}
> +	}
> +
> +	return !(flags & LIBEVDEV_RESOLVER_FLAG_REQUIRE_PREFIX);
> +}
> +
> +static const struct name_entry*
> +lookup_name(const struct name_entry *array, size_t asize,
> +	    struct name_lookup *lookup, enum libevdev_resolver_flag flags)
> +{
> +	const struct name_entry *entry;
> +
> +	entry = bsearch(lookup, array, asize, sizeof(*array), cmp_entry_case);
> +	if (!entry)
> +		return NULL;
> +
> +	if (flags & LIBEVDEV_RESOLVER_FLAG_CASE_SENSITIVE)
> +		if (strncmp(entry->name, lookup->name, lookup->len))
> +			return NULL;
> +
> +	return entry;
> +}
> +
> +LIBEVDEV_EXPORT int
> +libevdev_event_type_from_name(const char *name, ssize_t len,
> +			      enum libevdev_resolver_flag flags)
> +{
> +	struct name_lookup lookup;
> +	const struct name_entry *entry;
> +
> +	if (!fill_lookup(&lookup, name, len, "EV_", 3, flags))
> +		return -1;
> +
> +	entry = lookup_name(ev_names, ARRAY_LENGTH(ev_names), &lookup, flags);
> +
> +	return entry ? (int)entry->value : -1;
> +}
> +
> +LIBEVDEV_EXPORT int
> +libevdev_event_code_from_name(const char *name, ssize_t len, int type,
> +			      enum libevdev_resolver_flag flags)
> +{
> +	struct name_lookup lookup;
> +	const struct name_entry *entry, *table;
> +	size_t table_size, i;
> +	bool try_btn = false;
> +
> +	len = (len < 0) ? strlen(name) : (size_t)len;
> +
> +	if (type < 0) {
> +		for (i = 0; i < EV_MAX + 1; ++i) {
> +			if (!ev_map[i])
> +				continue;
> +			if (startswith_case(name, len, &ev_map[i][3],
> +					    strlen(&ev_map[i][3]))) {
> +				type = i;
> +				break;
> +			}
> +		}
> +
> +		if (startswith_case(name, len, "BTN_", 4))
> +			type = EV_KEY;
> +
> +		if (type < 0)
> +			return -1;
> +	}
> +
> +#define TYPE_ENTRY(_code, _prefix, _plen, _array) \
> +	case _code: \
> +		if (!fill_lookup(&lookup, name, len, _prefix, _plen, flags)) \
> +			return -1; \
> +		table = _array; \
> +		table_size = ARRAY_LENGTH(_array); \
> +		break
> +
> +	switch (type) {
> +		TYPE_ENTRY(EV_SYN, "SYN_", 4, syn_names);
> +		TYPE_ENTRY(EV_REL, "REL_", 4, rel_names);
> +		TYPE_ENTRY(EV_ABS, "ABS_", 4, abs_names);
> +		TYPE_ENTRY(EV_MSC, "MSC_", 4, msc_names);
> +		TYPE_ENTRY(EV_SND, "SND_", 4, snd_names);
> +		TYPE_ENTRY(EV_SW,  "SW_",  3, sw_names);
> +		TYPE_ENTRY(EV_LED, "LED_", 4, led_names);
> +		TYPE_ENTRY(EV_REP, "REP_", 4, rep_names);
> +		TYPE_ENTRY(EV_FF,  "FF_",  3, ff_names);
> +	case EV_KEY:
> +		table = key_names;
> +		table_size = ARRAY_LENGTH(key_names);
> +		lookup.name = name;
> +		lookup.len = len;
> +
> +		if (startswith_case(name, len, "KEY_", 4)) {
> +			if (!(flags & LIBEVDEV_RESOLVER_FLAG_CASE_SENSITIVE) ||
> +			    startswith(name, len, "KEY_", 4)) {
> +				lookup.name = &name[4];
> +				lookup.len = len - 4;
> +				break;
> +			}
> +		} else if (startswith_case(name, len, "BTN_", 4)) {
> +			if (!(flags & LIBEVDEV_RESOLVER_FLAG_CASE_SENSITIVE) ||
> +			    startswith(name, len, "BTN_", 4)) {
> +				lookup.name = &name[4];
> +				lookup.len = len - 4;
> +				try_btn = true;
> +				break;
> +			}
> +		}
> +
> +		if (flags & LIBEVDEV_RESOLVER_FLAG_REQUIRE_PREFIX)
> +			return -1;
> +
> +		try_btn = true;
> +		break;
> +	default:
> +		return -1;
> +	}
> +
> +#undef TYPE_ENTRY
> +
> +	entry = lookup_name(table, table_size, &lookup, flags);
> +	if (!entry && try_btn)
> +		entry = lookup_name(btn_names, ARRAY_LENGTH(btn_names),
> +				    &lookup, flags);
> +
> +	return entry ? (int)entry->value : -1;
> +}
> diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
> index 22d1e3d..07d9f7d 100644
> --- a/libevdev/libevdev.h
> +++ b/libevdev/libevdev.h
> @@ -1356,6 +1356,7 @@ int libevdev_event_is_code(const struct input_event *ev, unsigned int type, unsi
>   * defines for new properties libevdev will not automatically pick these up.
>   */
>  const char * libevdev_event_type_get_name(unsigned int type);
> +
>  /**
>   * @ingroup misc
>   *
> @@ -1400,6 +1401,60 @@ const char* libevdev_property_get_name(unsigned int prop);
>  int libevdev_event_type_get_max(unsigned int type);
>  
>  /**
> + * @ingroup misc
> + */
> +enum libevdev_resolver_flag {
> +	/** Default behavior for name resolution (no flags passed). */
> +	LIBEVDEV_RESOLVER_FLAG_DEFAULT			= 0,
> +	/** Require the resolver to compare names case sensitive */
> +	LIBEVDEV_RESOLVER_FLAG_CASE_SENSITIVE		= 1,
> +	/** Require the common prefix on the passed string */
> +	LIBEVDEV_RESOLVER_FLAG_REQUIRE_PREFIX		= 2,
> +};

I'd use LIBEVDEV_NAME_* since there is no use of "resolve" in the function
names themselves.

> +
> +/**
> + * @ingroup misc
> + *

can we have a one or two sentence summary of what the function does here? As
simple "convert from a string representation of an event type into the
matching type" would be enough.

> + * @param name A non-NULL string describing an input-event type ("EV_KEY",
> + * "EV_ABS", ...). The leading "EV_" prefix may be omitted.
> + * @param len The length of the passed string excluding any terminating 0
> + * character. If <0 the string is assumed to be zero-terminated.

"less than zero"

> + * @param flags A list of flags to control the name resolution.
> + *
> + * @return The given type constant for the passed name or -1 if not found.
> + *
> + * @note EV_MAX is also recognized.
> + */
> +int libevdev_event_type_from_name(const char *name, ssize_t len,
> +				  enum libevdev_resolver_flag flags);
> +
> +/**
> + * @ingroup misc

summary here too please.

> + *
> + * @param name A non-NULL string describing an input-event code (KEY_A,
> + * ABS_X, BTN_Y, ...). The leading "XYZ_" prefix may be omitted.
> + * @param len The length of the passed string excluding any terminating 0
> + * character. If <0 the string is assumed to be zero-terminated.
> + * @param type The EV_XYZ input event type to specify the group to search. If
> + * <0 then the prefix of @name is used to detect the type. If the prefix is not

this needs to be @p name, for the proper highlighting.

> + * specified, this will fail.
> + * @param flags A list of flags to control the name resolution.
> + *
> + * @return The given code constant for the passed name or -1 if not found.
> + *
> + * @note *_MAX codes are also recognized. Supported types currently are SYN,
> + * KEY, REL, ABS, MSC, SND, SW, LED, REP, FF. Note that BTN_* codes belong to
> + * the EV_KEY group.
> + *
> + * @note If you specify EV_KEY as type and omit the prefix of your name, this
> + * will first try to match against KEY_* names and then against BTN_* names.
> + * Hence, if your name exists in both groups, the KEY_* name is returned. You
> + * have to include the prefix if you pass ambigious names.

> + */
> +int libevdev_event_code_from_name(const char *name, ssize_t len, int type,
> +				  enum libevdev_resolver_flag flags);

all the *event_code* functions have the arguments in order type, code,
should be the same here.

Cheers,
   Peter

> +
> +/**
>   * @ingroup bits
>   *
>   * Get the repeat delay and repeat period values for this device.
> diff --git a/test/Makefile.am b/test/Makefile.am
> index 8454329..258af9e 100644
> --- a/test/Makefile.am
> +++ b/test/Makefile.am
> @@ -5,6 +5,7 @@ TESTS = $(noinst_PROGRAMS)
>  
>  libevdev_sources = $(top_srcdir)/libevdev/libevdev.c \
>  		   $(top_srcdir)/libevdev/libevdev.h \
> +		   $(top_srcdir)/libevdev/libevdev-names.c \
>  		   $(top_srcdir)/libevdev/libevdev-uinput.h \
>  		   $(top_srcdir)/libevdev/libevdev-uinput.c \
>  		   $(top_srcdir)/libevdev/libevdev-uinput-int.h \
> -- 
> 1.8.4
> 


More information about the Input-tools mailing list