evdev-rs 0.2.0

Bindings to libevdev for interacting with evdev devices. It moves the common tasks when dealing with evdev devices into a library and provides a library interface to the callers, thus avoiding erroneous ioctls, etc.
Documentation
#!/usr/bin/env python
# Parses linux/input.h scanning for #define KEY_FOO 134
# Prints Rust source header files that can be used for
# mapping and lookup tables.
#
# The original version of this file is in libevdev
#

from __future__ import print_function
import re
import sys

class Bits(object):
    pass

prefixes = [
    "EV_",
    "REL_",
    "ABS_",
    "KEY_",
    "BTN_",
    "LED_",
    "SND_",
    "MSC_",
    "SW_",
    "FF_",
    "SYN_",
    "REP_",
    "INPUT_PROP_",
    "BUS_"
]

blacklist = [
    "EV_VERSION",
    "BTN_MISC",
    "BTN_MOUSE",
    "BTN_JOYSTICK",
    "BTN_GAMEPAD",
    "BTN_DIGI",
    "BTN_WHEEL",
    "BTN_TRIGGER_HAPPY",
]

btn_additional = [
    [0, "BTN_A"],
    [0, "BTN_B"],
    [0, "BTN_X"],
    [0, "BTN_Y"],
]

event_names = [
    "REL_",
    "ABS_",
    "KEY_",
    "BTN_",
    "LED_",
    "SND_",
    "MSC_",
    "SW_",
    "FF_",
    "SYN_",
    "REP_",
]

def convert(name):
    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()

def print_enums(bits, prefix):
        if prefix == "ev":
                enum_name = "EventType"
        elif prefix == "input_prop":
                enum_name = "InputProp"
        elif prefix == "bus":
                enum_name = "BusType"
        else:
                enum_name = "EV_" + prefix.upper()

        if not hasattr(bits, prefix):
                return

        associated_names = []

        print("#[allow(non_camel_case_types)]")
        print("#[derive(Clone, Debug, PartialEq, Eq, Hash)]")
        print("pub enum %s {" % enum_name)
        for val, names in list(getattr(bits, prefix).items()):
            print("    %s = %s," % (names[0], val))
            if len(names) > 1:
                associated_names.extend([(names[0], names[1:])])
        if prefix == "key":
            for val, names in list(getattr(bits, "btn").items()):
                print("    %s = %s," % (names[0], val))
                if len(names) > 1:
                    associated_names.extend([(names[0], names[1:])])
        print("}");
        print("");

        if len(associated_names) > 0:
            print("impl %s {" % enum_name)
            for orig, names in associated_names:
                for name in names:
                    print("    pub const %s: %s = %s::%s;" % (name, enum_name, enum_name, orig))
            print("}")
            print("")

def print_enums_convert_fn(bits, prefix):
        if prefix == "ev":
                fn_name = "EventType"
        elif prefix == "input_prop":
                fn_name = "InputProp"
        elif prefix == "bus":
                fn_name = "BusType"
        else:
                fn_name = "EV_" + prefix.upper()

        if  not hasattr(bits, prefix):
                return

        print("pub fn %s(code: u32) -> Option<%s> {" %("int_to_" + convert(fn_name), fn_name))
        print("    match code {")
        for val, names in list(getattr(bits, prefix).items()):
            print("        %s => Some(%s::%s)," % (val, fn_name, names[0]))
        if prefix == "key":
                for val, names in list(getattr(bits, "btn").items()):
                    print("        %s => Some(%s::%s)," % (val, fn_name, names[0]))
        print("        _ => None")
        print("    }");
        print("}");
        print("");

def print_event_code(bits, prefix):
        if  not hasattr(bits, prefix):
                return

        print("#[allow(non_camel_case_types)]")
        print("#[derive(Clone, Debug, PartialEq)]")
        print("pub enum EventCode {")
        for val, [name] in list(getattr(bits, prefix).items()):
            if name[3:]+"_" in event_names:
                    print("    %s(%s)," % (name, name))
            elif name == "EV_FF_STATUS":
                    print("    EV_FF_STATUS(EV_FF),")
            else:
                    print("    %s," % (name))
        if prefix == "key":
            for val, names in list(getattr(bits, "btn").items()):
                for name in names:
                    print("    %s = %s," % (name, val))
        print("}");
        print("");

def print_lookup(bits, prefix):
    if not hasattr(bits, prefix):
        return

    names = list(getattr(bits, prefix).items())
    if prefix == "btn":
        names = names + btn_additional;

    for val, name in sorted(names, key=lambda e: e[1]):
        print("	{ .name = \"%s\", .value = %s }," % (name, name))

def print_lookup_table(bits):
    print("struct name_entry {")
    print("	const char *name;")
    print("	unsigned int value;")
    print("};")
    print("")
    print("static const struct name_entry ev_names[] = {")
    print_lookup(bits, "ev")
    print("};")
    print("")

    print("static const struct name_entry code_names[] = {")
    for prefix in sorted(names, key=lambda e: e):
        print_lookup(bits, prefix[:-1].lower())
    print("};")
    print("")
    print("static const struct name_entry prop_names[] = {")
    print_lookup(bits, "input_prop")
    print("};")
    print("")

def print_mapping_table(bits):
        for prefix in prefixes:
                if prefix == "BTN_":
                        continue
                print_enums(bits, prefix[:-1].lower())
                print_enums_convert_fn(bits, prefix[:-1].lower())
                if prefix == "EV_":
                        print_event_code(bits, prefix[:-1].lower())

def parse_define(bits, line):
    m = re.match(r"^#define\s+(\w+)\s+(\w+)", line)
    if m == None:
        return

    name = m.group(1)

    if name in blacklist:
        return

    try:
        value = int(m.group(2), 0)
    except ValueError:
        return

    for prefix in prefixes:
        if not name.startswith(prefix):
            continue

        attrname = prefix[:-1].lower()

        if not hasattr(bits, attrname):
            setattr(bits, attrname, {})
        b = getattr(bits, attrname)
        if value in b:
            b[value].append(name)
        else:
            b[value] = [name]

def parse(fp):
    bits = Bits()

    lines = fp.readlines()
    for line in lines:
        if not line.startswith("#define"):
            continue
        parse_define(bits, line)

    return bits

def usage(prog):
    print("Usage: %s /path/to/linux/input.h" % prog)

if __name__ == "__main__":
        if len(sys.argv) < 2:
                usage(sys.argv[0])
                sys.exit(2)

        print("/* THIS FILE IS GENERATED, DO NOT EDIT */")
        print("")

        if len(sys.argv) == 2:
                with open(sys.argv[1]) as f:
                        bits = parse(f)
                        print_mapping_table(bits)
                        sys.exit(2)

        for i in (1, len(sys.argv) - 1):
                with open(sys.argv[i]) as f:
                        bits = parse(f)
                        print_mapping_table(bits)