diff options
author | Keith Packard <keithp@keithp.com> | 2010-06-10 18:39:10 -0700 |
---|---|---|
committer | Keith Packard <keithp@keithp.com> | 2010-06-10 18:39:10 -0700 |
commit | 07a093add0b7e40c4d9b9b59273e3ff9e14a88a7 (patch) | |
tree | ed1dfc8d0051a15f417d41fa9d3e7f41d131559e | |
parent | 84190d20954ef7888e4d96832c2a4b3225b4dfa2 (diff) | |
parent | dc614484f93b67e8b62dbb1bb2fd247fe5a4c850 (diff) |
Merge remote branch 'whot/for-keith'
-rw-r--r-- | Xi/exevents.c | 4 | ||||
-rw-r--r-- | config/Makefile.am | 2 | ||||
-rw-r--r-- | config/dbus.c | 1 | ||||
-rwxr-xr-x | config/fdi2iclass.py | 202 | ||||
-rw-r--r-- | config/hal.c | 32 | ||||
-rw-r--r-- | config/udev.c | 62 | ||||
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | dix/inpututils.c | 6 | ||||
-rw-r--r-- | hw/xfree86/common/xf86Xinput.c | 222 | ||||
-rw-r--r-- | hw/xfree86/doc/man/xorg.conf.man.pre | 71 | ||||
-rw-r--r-- | hw/xfree86/parser/InputClass.c | 191 | ||||
-rw-r--r-- | hw/xfree86/parser/xf86Parser.h | 20 | ||||
-rw-r--r-- | hw/xfree86/parser/xf86tokens.h | 4 | ||||
-rw-r--r-- | include/dix-config.h.in | 3 | ||||
-rw-r--r-- | include/input.h | 2 | ||||
-rw-r--r-- | test/input.c | 24 |
16 files changed, 713 insertions, 138 deletions
diff --git a/Xi/exevents.c b/Xi/exevents.c index a609c7393..de24b4cc1 100644 --- a/Xi/exevents.c +++ b/Xi/exevents.c @@ -193,16 +193,12 @@ void CopyKeyClass(DeviceIntPtr device, DeviceIntPtr master) { KeyClassPtr mk = master->key; - KeyClassPtr dk = device->key; - int i; if (device == master) return; mk->sourceid = device->id; - for (i = 0; i < 8; i++) - mk->modifierKeyCount[i] = dk->modifierKeyCount[i]; if (!XkbCopyDeviceKeymap(master, device)) FatalError("Couldn't pivot keymap from device to core!\n"); diff --git a/config/Makefile.am b/config/Makefile.am index 675a3b260..e1f1c4e16 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -36,4 +36,4 @@ endif # CONFIG_NEED_DBUS endif # !CONFIG_UDEV -EXTRA_DIST = xorg-server.conf x11-input.fdi 10-evdev.conf +EXTRA_DIST = xorg-server.conf x11-input.fdi 10-evdev.conf fdi2iclass.py diff --git a/config/dbus.c b/config/dbus.c index 4888f621c..b67fddb3e 100644 --- a/config/dbus.c +++ b/config/dbus.c @@ -27,7 +27,6 @@ #include <dix-config.h> #endif -#define DBUS_API_SUBJECT_TO_CHANGE #include <dbus/dbus.h> #include <string.h> diff --git a/config/fdi2iclass.py b/config/fdi2iclass.py new file mode 100755 index 000000000..897444068 --- /dev/null +++ b/config/fdi2iclass.py @@ -0,0 +1,202 @@ +#!/usr/bin/python +# +# Convert xorg keys from hal FDIs files to xorg.conf InputClass sections. +# Modified from Martin Pitt's original fdi2mpi.py script: +# http://cgit.freedesktop.org/media-player-info/tree/tools/fdi2mpi.py +# +# (C) 2010 Dan Nicholson +# (C) 2009 Canonical Ltd. +# Author: Dan Nicholson <dbn.lists@gmail.com> +# Author: Martin Pitt <martin.pitt@ubuntu.com> +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# fur- nished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FIT- NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON- +# NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import sys, xml.dom.minidom + +# dict converting <match> tags to Match* entries +match_table = { + 'info.product': 'MatchProduct', + 'input.product': 'MatchProduct', + 'info.vendor': 'MatchVendor', + 'input.vendor': 'MatchVendor', + 'info.device': 'MatchDevicePath', + 'linux.device_file': 'MatchDevicePath', + '/org/freedesktop/Hal/devices/computer:system.kernel.name': 'MatchOS', + '@info.parent:pnp.id': 'MatchPnPID', +} + +# dict converting info.capabilities list to Match* entries +cap_match_table = { + 'input.keys': 'MatchIsKeyboard', + 'input.keyboard': 'MatchIsKeyboard', + 'input.keypad': 'MatchIsKeyboard', + 'input.mouse': 'MatchIsPointer', + 'input.joystick': 'MatchIsJoystick', + 'input.tablet': 'MatchIsTablet', + 'input.touchpad': 'MatchIsTouchpad', + 'input.touchscreen': 'MatchIsTouchscreen', +} + +def device_glob(path): + '''Convert a contains device path to a glob entry''' + if path[0] != '/': + path = '*' + path + return path + '*' + +def parse_match(node): + '''Parse a <match> tag to a tuple with InputClass values''' + match = None + value = None + booltype = False + + # see what type of key we have + if node.attributes.has_key('key'): + key = node.attributes['key'].nodeValue + if key in match_table: + match = match_table[key] + elif key == 'info.capabilities': + booltype = True + + # bail out now if it's unrecognized + if not match and not booltype: + return (match, value) + + if node.attributes.has_key('string'): + value = node.attributes['string'].nodeValue + elif node.attributes.has_key('contains'): + value = node.attributes['contains'].nodeValue + if match == 'MatchDevicePath': + value = device_glob(value) + elif booltype and value in cap_match_table: + match = cap_match_table[value] + value = 'yes' + elif node.attributes.has_key('string_outof'): + value = node.attributes['string_outof'].nodeValue.replace(';','|') + elif node.attributes.has_key('contains_outof'): + all_values = node.attributes['contains_outof'].nodeValue.split(';') + for v in all_values: + if match == 'MatchDevicePath': + v = device_glob(v) + elif match == 'MatchPnPID' and len(v) < 7: + v += '*' + if value: + value += '|' + v + else: + value = v + + return (match, value) + +def parse_options(node): + '''Parse the x11_* options and return InputClass entries''' + driver = '' + ignore = False + options = [] + for n in node.childNodes: + if n.nodeType != xml.dom.minidom.Node.ELEMENT_NODE: + continue + + tag = n.tagName + key = n.attributes['key'].nodeValue + value = '' + + if n.hasChildNodes(): + content_node = n.childNodes[0] + assert content_node.nodeType == xml.dom.Node.TEXT_NODE + value = content_node.nodeValue + + if tag == 'match': + continue + assert tag in ('addset', 'merge', 'append', 'remove') + + if tag == 'remove' and key == 'input.x11_driver': + ignore = True + elif key == 'input.x11_driver': + driver = value + elif key.startswith('input.x11_options.'): + option = key.split('.', 2)[2] + options.append((option, value)) + + return (driver, ignore, options) + +def is_match_node(node): + '''Check if a node is a <match> element''' + return node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE and \ + node.tagName == 'match' + +def parse_all_matches(node): + '''Parse a x11 match tag and any parents that don't supply their + own options''' + matches = [] + + while True: + (key, value) = parse_match(node) + if key and value: + matches.append((key, value)) + + # walk up to a parent match node + node = node.parentNode + if node == None or not is_match_node(node): + break + + # leave if there other options at this level + children = set([n.tagName for n in node.childNodes + if n.nodeType == xml.dom.minidom.Node.ELEMENT_NODE]) + if children & set(['addset', 'merge', 'append']): + break + + return matches + +# stupid counter to give "unique" rule names +num_sections = 1 +def print_section(matches, driver, ignore, options): + '''Print a valid InputClass section to stdout''' + global num_sections + print 'Section "InputClass"' + print '\tIdentifier "Converted Class %d"' % num_sections + num_sections += 1 + for m, v in matches: + print '\t%s "%s"' % (m, v) + if driver: + print '\tDriver "%s"' % driver + if ignore: + print '\tOption "Ignore" "yes"' + for o, v in options: + print '\tOption "%s" "%s"' % (o, v) + print 'EndSection' + +def parse_fdi(fdi): + '''Parse x11 matches from fdi''' + # find all <match> leaf nodes + num = 0 + for match_node in fdi.getElementsByTagName('match'): + children = set([n.tagName for n in match_node.childNodes + if n.nodeType == xml.dom.minidom.Node.ELEMENT_NODE]) + + # see if there are any options at this level + (driver, ignore, options) = parse_options(match_node) + if not driver and not ignore and not options: + continue + + matches = parse_all_matches(match_node) + if num > 0: + print + print_section(matches, driver, ignore, options) + num += 1 + +for f in sys.argv[1:]: + parse_fdi(xml.dom.minidom.parse(f)) diff --git a/config/hal.c b/config/hal.c index 5938d4e24..8f9aeb8d3 100644 --- a/config/hal.c +++ b/config/hal.c @@ -129,6 +129,7 @@ static void device_added(LibHalContext *hal_ctx, const char *udi) { char *path = NULL, *driver = NULL, *name = NULL, *config_info = NULL; + char *hal_tags, *parent; InputOption *options = NULL, *tmpo = NULL; InputAttributes attrs = {0}; DeviceIntPtr dev = NULL; @@ -164,7 +165,9 @@ device_added(LibHalContext *hal_ctx, const char *udi) attrs.product = xstrdup(name); attrs.vendor = get_prop_string(hal_ctx, udi, "info.vendor"); - attrs.tags = xstrtokenize(get_prop_string(hal_ctx, udi, "input.tags"), ","); + hal_tags = get_prop_string(hal_ctx, udi, "input.tags"); + attrs.tags = xstrtokenize(hal_tags, ","); + free(hal_tags); if (libhal_device_query_capability(hal_ctx, udi, "input.keys", NULL)) attrs.flags |= ATTR_KEYBOARD; @@ -179,6 +182,29 @@ device_added(LibHalContext *hal_ctx, const char *udi) if (libhal_device_query_capability(hal_ctx, udi, "input.touchscreen", NULL)) attrs.flags |= ATTR_TOUCHSCREEN; + parent = get_prop_string(hal_ctx, udi, "info.parent"); + if (parent) { + int usb_vendor, usb_product; + + attrs.pnp_id = get_prop_string(hal_ctx, parent, "pnp.id"); + + /* construct USB ID in lowercase - "0000:ffff" */ + usb_vendor = libhal_device_get_property_int(hal_ctx, parent, + "usb.vendor_id", NULL); + LogMessageVerb(X_INFO, 10, + "config/hal: getting usb.vendor_id on %s " + "returned %04x\n", parent, usb_vendor); + usb_product = libhal_device_get_property_int(hal_ctx, parent, + "usb.product_id", NULL); + LogMessageVerb(X_INFO, 10, + "config/hal: getting usb.product_id on %s " + "returned %04x\n", parent, usb_product); + if (usb_vendor && usb_product) + attrs.usb_id = Xprintf("%04x:%04x", usb_vendor, usb_product); + + free(parent); + } + options = calloc(sizeof(*options), 1); if (!options){ LogMessage(X_ERROR, "config/hal: couldn't allocate space for input options!\n"); @@ -381,6 +407,8 @@ unwind: free(attrs.product); free(attrs.vendor); free(attrs.device); + free(attrs.pnp_id); + free(attrs.usb_id); if (attrs.tags) { char **tag = attrs.tags; while (*tag) { @@ -619,7 +647,7 @@ config_hal_init(void) } /* verbose message */ - LogMessageVerb(X_INFO,7,"config/hal: initialized"); + LogMessageVerb(X_INFO,7,"config/hal: initialized\n"); return 1; } diff --git a/config/udev.c b/config/udev.c index 5e8d8dab0..16c462455 100644 --- a/config/udev.c +++ b/config/udev.c @@ -28,6 +28,7 @@ #endif #include <libudev.h> +#include <ctype.h> #include "input.h" #include "inputstr.h" @@ -37,6 +38,17 @@ #define UDEV_XKB_PROP_KEY "xkb" +#define LOG_PROPERTY(path, prop, val) \ + LogMessageVerb(X_INFO, 10, \ + "config/udev: getting property %s on %s " \ + "returned \"%s\"\n", \ + (prop), (path), (val) ? (val) : "(null)") +#define LOG_SYSATTR(path, attr, val) \ + LogMessageVerb(X_INFO, 10, \ + "config/udev: getting attribute %s on %s " \ + "returned \"%s\"\n", \ + (attr), (path), (val) ? (val) : "(null)") + static struct udev_monitor *udev_monitor; static void @@ -45,6 +57,8 @@ device_added(struct udev_device *udev_device) const char *path, *name = NULL; char *config_info = NULL; const char *syspath; + const char *tags_prop; + const char *usb_vendor = NULL, *usb_model = NULL; const char *key, *value, *tmp; InputOption *options = NULL, *tmpo; InputAttributes attrs = {}; @@ -60,8 +74,13 @@ device_added(struct udev_device *udev_device) if (!path || !syspath) return; - if (!udev_device_get_property_value(udev_device, "ID_INPUT")) + if (!udev_device_get_property_value(udev_device, "ID_INPUT")) { + LogMessageVerb(X_INFO, 10, + "config/udev: ignoring device %s without " + "property ID_INPUT set\n", + path); return; + } options = calloc(sizeof(*options), 1); if (!options) @@ -74,9 +93,17 @@ device_added(struct udev_device *udev_device) parent = udev_device_get_parent(udev_device); if (parent) { + const char *ppath = udev_device_get_devnode(parent); + name = udev_device_get_sysattr_value(parent, "name"); - if (!name) + LOG_SYSATTR(ppath, "name", name); + if (!name) { name = udev_device_get_property_value(parent, "NAME"); + LOG_PROPERTY(ppath, "NAME", name); + } + + attrs.pnp_id = udev_device_get_sysattr_value(parent, "id"); + LOG_SYSATTR(ppath, "id", attrs.pnp_id); } if (!name) name = "(unnamed)"; @@ -87,7 +114,10 @@ device_added(struct udev_device *udev_device) add_option(&options, "path", path); add_option(&options, "device", path); attrs.device = path; - attrs.tags = xstrtokenize(udev_device_get_property_value(udev_device, "ID_INPUT.tags"), ","); + + tags_prop = udev_device_get_property_value(udev_device, "ID_INPUT.tags"); + LOG_PROPERTY(path, "ID_INPUT.tags", tags_prop); + attrs.tags = xstrtokenize(tags_prop, ","); config_info = Xprintf("udev:%s", syspath); if (!config_info) @@ -107,6 +137,7 @@ device_added(struct udev_device *udev_device) value = udev_list_entry_get_value(entry); if (!strncasecmp(key, UDEV_XKB_PROP_KEY, sizeof(UDEV_XKB_PROP_KEY) - 1)) { + LOG_PROPERTY(path, key, value); tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1; if (!strcasecmp(tmp, "rules")) add_option(&options, "xkb_rules", value); @@ -119,21 +150,45 @@ device_added(struct udev_device *udev_device) else if (!strcasecmp(tmp, "options")) add_option(&options, "xkb_options", value); } else if (!strcmp(key, "ID_VENDOR")) { + LOG_PROPERTY(path, key, value); attrs.vendor = value; + } else if (!strcmp(key, "ID_VENDOR_ID")) { + LOG_PROPERTY(path, key, value); + usb_vendor = value; + } else if (!strcmp(key, "ID_VENDOR_MODEL")) { + LOG_PROPERTY(path, key, value); + usb_model = value; } else if (!strcmp(key, "ID_INPUT_KEY")) { + LOG_PROPERTY(path, key, value); attrs.flags |= ATTR_KEYBOARD; } else if (!strcmp(key, "ID_INPUT_MOUSE")) { + LOG_PROPERTY(path, key, value); attrs.flags |= ATTR_POINTER; } else if (!strcmp(key, "ID_INPUT_JOYSTICK")) { + LOG_PROPERTY(path, key, value); attrs.flags |= ATTR_JOYSTICK; } else if (!strcmp(key, "ID_INPUT_TABLET")) { + LOG_PROPERTY(path, key, value); attrs.flags |= ATTR_TABLET; } else if (!strcmp(key, "ID_INPUT_TOUCHPAD")) { + LOG_PROPERTY(path, key, value); attrs.flags |= ATTR_TOUCHPAD; } else if (!strcmp(key, "ID_INPUT_TOUCHSCREEN")) { + LOG_PROPERTY(path, key, value); attrs.flags |= ATTR_TOUCHSCREEN; } } + + /* construct USB ID in lowercase hex - "0000:ffff" */ + if (usb_vendor && usb_model) { + attrs.usb_id = Xprintf("%s:%s", usb_vendor, usb_model); + if (attrs.usb_id) { + char *cur; + for (cur = attrs.usb_id; *cur; cur++) + *cur = tolower(*cur); + } + } + LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n", name, path); rc = NewInputDeviceRequest(options, &attrs, &dev); @@ -154,6 +209,7 @@ device_added(struct udev_device *udev_device) free(tmpo); } + free(attrs.usb_id); if (attrs.tags) { char **tag = attrs.tags; while (*tag) { diff --git a/configure.ac b/configure.ac index c8b49debb..02660a6b4 100644 --- a/configure.ac +++ b/configure.ac @@ -123,7 +123,7 @@ AM_CONDITIONAL(SPECIAL_DTRACE_OBJECTS, [test "x$SPECIAL_DTRACE_OBJECTS" = "xyes" AC_HEADER_DIRENT AC_HEADER_STDC -AC_CHECK_HEADERS([fcntl.h stdlib.h string.h unistd.h dlfcn.h stropts.h fnmatch.h]) +AC_CHECK_HEADERS([fcntl.h stdlib.h string.h unistd.h dlfcn.h stropts.h fnmatch.h sys/utsname.h]) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST @@ -809,6 +809,7 @@ LIBPCIACCESS="pciaccess >= 0.8.0" LIBGLIB="glib-2.0 >= 2.16" LIBUDEV="libudev >= 143" LIBSELINUX="libselinux >= 2.0.86" +LIBDBUS="dbus-1 >= 1.0" if test "x$CONFIG_UDEV" = xyes && { test "x$CONFIG_DBUS_API" = xyes || test "x$CONFIG_HAL" = xyes; }; then @@ -832,7 +833,7 @@ fi dnl HAVE_DBUS is true if we actually have the D-Bus library, whereas dnl CONFIG_DBUS_API is true if we want to enable the D-Bus config dnl API. -PKG_CHECK_MODULES(DBUS, dbus-1, [HAVE_DBUS=yes], [HAVE_DBUS=no]) +PKG_CHECK_MODULES(DBUS, $LIBDBUS, [HAVE_DBUS=yes], [HAVE_DBUS=no]) if test "x$HAVE_DBUS" = xyes; then AC_DEFINE(HAVE_DBUS, 1, [Have D-Bus support]) fi diff --git a/dix/inpututils.c b/dix/inpututils.c index df2ace0bb..8ec80b5e8 100644 --- a/dix/inpututils.c +++ b/dix/inpututils.c @@ -357,6 +357,10 @@ DuplicateInputAttributes(InputAttributes *attrs) goto unwind; if (attrs->device && !(new_attr->device = strdup(attrs->device))) goto unwind; + if (attrs->pnp_id && !(new_attr->pnp_id = strdup(attrs->pnp_id))) + goto unwind; + if (attrs->usb_id && !(new_attr->usb_id = strdup(attrs->usb_id))) + goto unwind; new_attr->flags = attrs->flags; @@ -401,6 +405,8 @@ FreeInputAttributes(InputAttributes *attrs) free(attrs->product); free(attrs->vendor); free(attrs->device); + free(attrs->pnp_id); + free(attrs->usb_id); if ((tags = attrs->tags)) while(*tags) diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c index 889339a9e..76d2d0041 100644 --- a/hw/xfree86/common/xf86Xinput.c +++ b/hw/xfree86/common/xf86Xinput.c @@ -80,6 +80,9 @@ #ifdef HAVE_FNMATCH_H #include <fnmatch.h> #endif +#ifdef HAVE_SYS_UTSNAME_H +#include <sys/utsname.h> +#endif #include "extnsionst.h" @@ -497,73 +500,152 @@ AddOtherInputDevices(void) } /* - * Classes without any Match statements match all devices. Otherwise, all - * statements must match. + * Get the operating system name from uname and store it statically to avoid + * repeating the system call each time MatchOS is checked. */ -static Bool -InputClassMatches(XF86ConfInputClassPtr iclass, InputAttributes *attrs) +static const char * +HostOS(void) { - char **cur; - Bool match; - - if (iclass->match_product) { - if (!attrs->product) - return FALSE; - /* see if any of the values match */ - for (cur = iclass->match_product, match = FALSE; *cur; cur++) - if (strstr(attrs->product, *cur)) { - match = TRUE; - break; - } - if (!match) - return FALSE; - } - if (iclass->match_vendor) { - if (!attrs->vendor) - return FALSE; - /* see if any of the values match */ - for (cur = iclass->match_vendor, match = FALSE; *cur; cur++) - if (strstr(attrs->vendor, *cur)) { - match = TRUE; - break; - } - if (!match) - return FALSE; +#ifdef HAVE_SYS_UTSNAME_H + struct utsname name; + static char host_os[sizeof(name.sysname)] = ""; + + if (*host_os == '\0') { + if (uname(&name) >= 0) + strcpy(host_os, name.sysname); + else { + strncpy(host_os, "unknown", sizeof(host_os)); + host_os[sizeof(host_os)-1] = '\0'; + } } - if (iclass->match_device) { - if (!attrs->device) - return FALSE; - /* see if any of the values match */ - for (cur = iclass->match_device, match = FALSE; *cur; cur++) + return host_os; +#else + return ""; +#endif +} + +static int +match_substring(const char *attr, const char *pattern) +{ + return (strstr(attr, pattern)) ? 0 : -1; +} + +#ifdef HAVE_FNMATCH_H +static int +match_pattern(const char *attr, const char *pattern) +{ + return fnmatch(pattern, attr, 0); +} +#else +#define match_pattern match_substring +#endif + #ifdef HAVE_FNMATCH_H - if (fnmatch(*cur, attrs->device, FNM_PATHNAME) == 0) { +static int +match_path_pattern(const char *attr, const char *pattern) +{ + return fnmatch(pattern, attr, FNM_PATHNAME); +} #else - if (strstr(attrs->device, *cur)) { +#define match_path_pattern match_substring #endif + +/* + * Match an attribute against a list of NULL terminated arrays of patterns. + * If a pattern in each list entry is matched, return TRUE. + */ +static Bool +MatchAttrToken(const char *attr, struct list *patterns, + int (*compare)(const char *attr, const char *pattern)) +{ + const xf86MatchGroup *group; + + /* If there are no patterns, accept the match */ + if (list_is_empty(patterns)) + return TRUE; + + /* If there are patterns but no attribute, reject the match */ + if (!attr) + return FALSE; + + /* + * Otherwise, iterate the list of patterns ensuring each entry has a + * match. Each list entry is a separate Match line of the same type. + */ + list_for_each_entry(group, patterns, entry) { + char * const *cur; + Bool match = FALSE; + + for (cur = group->values; *cur; cur++) + if ((*compare)(attr, *cur) == 0) { match = TRUE; break; } if (!match) return FALSE; } - if (iclass->match_tag) { + + /* All the entries in the list matched the attribute */ + return TRUE; +} + +/* + * Classes without any Match statements match all devices. Otherwise, all + * statements must match. + */ +static Bool +InputClassMatches(const XF86ConfInputClassPtr iclass, const IDevPtr idev, + const InputAttributes *attrs) +{ + /* MatchProduct substring */ + if (!MatchAttrToken(attrs->product, &iclass->match_product, match_substring)) + return FALSE; + + /* MatchVendor substring */ + if (!MatchAttrToken(attrs->vendor, &iclass->match_vendor, match_substring)) + return FALSE; + + /* MatchDevicePath pattern */ + if (!MatchAttrToken(attrs->device, &iclass->match_device, match_path_pattern)) + return FALSE; + + /* MatchOS case-insensitive string */ + if (!MatchAttrToken(HostOS(), &iclass->match_os, strcasecmp)) + return FALSE; + + /* MatchPnPID pattern */ + if (!MatchAttrToken(attrs->pnp_id, &iclass->match_pnpid, match_pattern)) + return FALSE; + + /* MatchUSBID pattern */ + if (!MatchAttrToken(attrs->usb_id, &iclass->match_usbid, match_pattern)) + return FALSE; + + /* MatchDriver string */ + if (!MatchAttrToken(idev->driver, &iclass->match_driver, strcmp)) + return FALSE; + + /* + * MatchTag string + * See if any of the device's tags match any of the MatchTag tokens. + */ + if (!list_is_empty(&iclass->match_tag)) { + char * const *tag; + Bool match; + if (!attrs->tags) return FALSE; - - for (cur = iclass->match_tag, match = FALSE; *cur && !match; cur++) { - char * const *tag; - for(tag = attrs->tags; *tag; tag++) { - if (!strcmp(*tag, *cur)) { - match = TRUE; - break; - } + for (tag = attrs->tags, match = FALSE; *tag; tag++) { + if (MatchAttrToken(*tag, &iclass->match_tag, strcmp)) { + match = TRUE; + break; } } - if (!match) return FALSE; } + /* MatchIs* booleans */ if (iclass->is_keyboard.set && iclass->is_keyboard.val != !!(attrs->flags & ATTR_KEYBOARD)) return FALSE; @@ -582,6 +664,7 @@ InputClassMatches(XF86ConfInputClassPtr iclass, InputAttributes *attrs) if (iclass->is_touchscreen.set && iclass->is_touchscreen.val != !!(attrs->flags & ATTR_TOUCHSCREEN)) return FALSE; + return TRUE; } @@ -591,37 +674,36 @@ InputClassMatches(XF86ConfInputClassPtr iclass, InputAttributes *attrs) * well as any previous InputClass sections. */ static int -MergeInputClasses(IDevPtr idev, InputAttributes *attrs) +MergeInputClasses(const IDevPtr idev, const InputAttributes *attrs) { XF86ConfInputClassPtr cl; - XF86OptionPtr classopts, mergedopts = NULL; - char *classdriver = NULL; + XF86OptionPtr classopts; for (cl = xf86configptr->conf_inputclass_lst; cl; cl = cl->list.next) { - if (!InputClassMatches(cl, attrs)) + if (!InputClassMatches(cl, idev, attrs)) continue; - /* Collect class options and merge over previous classes */ + /* Collect class options and driver settings */ + classopts = xf86optionListDup(cl->option_lst); + if (cl->driver) { + free(idev->driver); + idev->driver = xstrdup(cl->driver); + if (!idev->driver) { + xf86Msg(X_ERROR, "Failed to allocate memory while merging " + "InputClass configuration"); + return BadAlloc; + } + classopts = xf86ReplaceStrOption(classopts, "driver", + idev->driver); + } + + /* Apply options to device with InputClass settings preferred. */ xf86Msg(X_CONFIG, "%s: Applying InputClass \"%s\"\n", idev->identifier, cl->identifier); - if (cl->driver) - classdriver = cl->driver; - classopts = xf86optionListDup(cl->option_lst); - mergedopts = xf86optionListMerge(mergedopts, classopts); + idev->commonOptions = xf86optionListMerge(idev->commonOptions, + classopts); } - /* Apply options to device with InputClass settings preferred. */ - if (classdriver) { - free(idev->driver); - idev->driver = xstrdup(classdriver); - if (!idev->driver) { - xf86Msg(X_ERROR, "Failed to allocate memory while merging " - "InputClass configuration"); - return BadAlloc; - } - mergedopts = xf86ReplaceStrOption(mergedopts, "driver", idev->driver); - } - idev->commonOptions = xf86optionListMerge(idev->commonOptions, mergedopts); return Success; } @@ -630,14 +712,14 @@ MergeInputClasses(IDevPtr idev, InputAttributes *attrs) * value of the last matching class and holler when returning TRUE. */ static Bool -IgnoreInputClass(IDevPtr idev, InputAttributes *attrs) +IgnoreInputClass(const IDevPtr idev, const InputAttributes *attrs) { XF86ConfInputClassPtr cl; Bool ignore = FALSE; const char *ignore_class; for (cl = xf86configptr->conf_inputclass_lst; cl; cl = cl->list.next) { - if (!InputClassMatches(cl, attrs)) + if (!InputClassMatches(cl, idev, attrs)) continue; if (xf86findOption(cl->option_lst, "Ignore")) { ignore = xf86CheckBoolOption(cl->option_lst, "Ignore", FALSE); diff --git a/hw/xfree86/doc/man/xorg.conf.man.pre b/hw/xfree86/doc/man/xorg.conf.man.pre index 9075db64f..6b3636fff 100644 --- a/hw/xfree86/doc/man/xorg.conf.man.pre +++ b/hw/xfree86/doc/man/xorg.conf.man.pre @@ -1065,35 +1065,85 @@ of the class. If none of the optional entries appear, the .B InputClass section is generic and will match any input device. If more than one of these entries appear, they all must match for the configuration to apply. -The allowed matching entries are shown below. .PP +There are two types of match entries used in +.B InputClass +sections. The first allows various tokens to be matched against attributes +of the device. An entry can be constructed to match attributes from different +devices by separating arguments with a '|' character. Multiple entries of the +same type may be supplied to add multiple matching conditions on the same +attribute. For example: +.PP +.RS 4 +.nf +.B "Section \*qInputClass\*q" +.B " Identifier \*qMy Class\*q" +.B " # product string must contain example and +.B " # either gizmo or gadget +.B " MatchProduct \*qexample\*q +.B " MatchProduct \*qgizmo|gadget\*q +.I " ..." +.B "EndSection" +.fi +.RE .TP 7 .BI "MatchProduct \*q" matchproduct \*q This entry can be used to check if the substring .RI \*q matchproduct \*q -occurs in the device's product name. Multiple substrings can be matched by -separating arguments with a '|' character. +occurs in the device's product name. .TP 7 .BI "MatchVendor \*q" matchvendor \*q This entry can be used to check if the substring .RI \*q matchvendor \*q -occurs in the device's vendor name. Multiple substrings can be matched by -separating arguments with a '|' character. +occurs in the device's vendor name. .TP 7 .BI "MatchDevicePath \*q" matchdevice \*q This entry can be used to check if the device file matches the .RI \*q matchdevice \*q -pathname pattern. Multiple patterns can be matched by separating arguments -with a '|' character. +pathname pattern. +.TP 7 +.BI "MatchOS \*q" matchos \*q +This entry can be used to check if the operating system matches the +case-insensitive +.RI \*q matchos \*q +string. This entry is only supported on platforms providing the +.BR uname (2) +system call. +.TP 7 +.BI "MatchPnPID \*q" matchpnp \*q +The device's Plug and Play (PnP) ID can be checked against the +.RI \*q matchpnp \*q +shell wildcard pattern. +.TP 7 +.BI "MatchUSBID \*q" matchusb \*q +The device's USB ID can be checked against the +.RI \*q matchusb \*q +shell wildcard pattern. The ID is constructed as lowercase hexadecimal numbers +separated by a ':'. This is the same format as the +.BR lsusb (8) +program. +.TP 7 +.BI "MatchDriver \*q" matchdriver \*q +Check the case-sensitive string +.RI \*q matchdriver \*q +against the currently configured driver of the device. Ordering of sections +using this entry is important since it will not match unless the driver has +been set by the config backend or a previous +.B InputClass +section. .TP 7 .BI "MatchTag \*q" matchtag \*q This entry can be used to check if tags assigned by the config backend matches the .RI \*q matchtag \*q -pattern. Multiple patterns can be matched by separating arguments -with a '|' character. A match is found if at least one of the tags given in +pattern. A match is found if at least one of the tags given in .RI \*q matchtag \*q matches at least one of the tags assigned by the backend. +.PP +The second type of entry is used to match device types. These entries take a +boolean argument similar to +.B Option +entries. .TP 7 .BI "MatchIsKeyboard \*q" bool \*q .TP 7 @@ -1106,9 +1156,6 @@ matches at least one of the tags assigned by the backend. .BI "MatchIsTouchpad \*q" bool \*q .TP 7 .BI "MatchIsTouchscreen \*q" bool \*q -Match device types. These entries take a boolean argument similar to -.B Option -entries. .PP When an input device has been matched to the .B InputClass diff --git a/hw/xfree86/parser/InputClass.c b/hw/xfree86/parser/InputClass.c index 7fb2866cd..ce611d990 100644 --- a/hw/xfree86/parser/InputClass.c +++ b/hw/xfree86/parser/InputClass.c @@ -47,6 +47,10 @@ xf86ConfigSymTabRec InputClassTab[] = {MATCH_PRODUCT, "matchproduct"}, {MATCH_VENDOR, "matchvendor"}, {MATCH_DEVICE_PATH, "matchdevicepath"}, + {MATCH_OS, "matchos"}, + {MATCH_PNPID, "matchpnpid"}, + {MATCH_USBID, "matchusbid"}, + {MATCH_DRIVER, "matchdriver"}, {MATCH_TAG, "matchtag"}, {MATCH_IS_KEYBOARD, "matchiskeyboard"}, {MATCH_IS_POINTER, "matchispointer"}, @@ -61,6 +65,18 @@ xf86ConfigSymTabRec InputClassTab[] = #define TOKEN_SEP "|" +static void +add_group_entry(struct list *head, char **values) +{ + xf86MatchGroup *group; + + group = malloc(sizeof(*group)); + if (group) { + group->values = values; + list_add(&group->entry, head); + } +} + XF86ConfInputClassPtr xf86parseInputClassSection(void) { @@ -69,6 +85,16 @@ xf86parseInputClassSection(void) parsePrologue(XF86ConfInputClassPtr, XF86ConfInputClassRec) + /* Initialize MatchGroup lists */ + list_init(&ptr->match_product); + list_init(&ptr->match_vendor); + list_init(&ptr->match_device); + list_init(&ptr->match_os); + list_init(&ptr->match_pnpid); + list_init(&ptr->match_usbid); + list_init(&ptr->match_driver); + list_init(&ptr->match_tag); + while ((token = xf86getToken(InputClassTab)) != ENDSECTION) { switch (token) { case COMMENT: @@ -96,22 +122,50 @@ xf86parseInputClassSection(void) case MATCH_PRODUCT: if (xf86getSubToken(&(ptr->comment)) != STRING) Error(QUOTE_MSG, "MatchProduct"); - ptr->match_product = xstrtokenize(val.str, TOKEN_SEP); + add_group_entry(&ptr->match_product, + xstrtokenize(val.str, TOKEN_SEP)); break; case MATCH_VENDOR: if (xf86getSubToken(&(ptr->comment)) != STRING) Error(QUOTE_MSG, "MatchVendor"); - ptr->match_vendor = xstrtokenize(val.str, TOKEN_SEP); + add_group_entry(&ptr->match_vendor, + xstrtokenize(val.str, TOKEN_SEP)); break; case MATCH_DEVICE_PATH: if (xf86getSubToken(&(ptr->comment)) != STRING) Error(QUOTE_MSG, "MatchDevicePath"); - ptr->match_device = xstrtokenize(val.str, TOKEN_SEP); + add_group_entry(&ptr->match_device, + xstrtokenize(val.str, TOKEN_SEP)); + break; + case MATCH_OS: + if (xf86getSubToken(&(ptr->comment)) != STRING) + Error(QUOTE_MSG, "MatchOS"); + add_group_entry(&ptr->match_os, + xstrtokenize(val.str, TOKEN_SEP)); + break; + case MATCH_PNPID: + if (xf86getSubToken(&(ptr->comment)) != STRING) + Error(QUOTE_MSG, "MatchPnPID"); + add_group_entry(&ptr->match_pnpid, + xstrtokenize(val.str, TOKEN_SEP)); + break; + case MATCH_USBID: + if (xf86getSubToken(&(ptr->comment)) != STRING) + Error(QUOTE_MSG, "MatchUSBID"); + add_group_entry(&ptr->match_usbid, + xstrtokenize(val.str, TOKEN_SEP)); + break; + case MATCH_DRIVER: + if (xf86getSubToken(&(ptr->comment)) != STRING) + Error(QUOTE_MSG, "MatchDriver"); + add_group_entry(&ptr->match_driver, + xstrtokenize(val.str, TOKEN_SEP)); break; case MATCH_TAG: if (xf86getSubToken(&(ptr->comment)) != STRING) Error(QUOTE_MSG, "MatchTag"); - ptr->match_tag = xstrtokenize(val.str, TOKEN_SEP); + add_group_entry(&ptr->match_tag, + xstrtokenize(val.str, TOKEN_SEP)); break; case MATCH_IS_KEYBOARD: if (xf86getSubToken(&(ptr->comment)) != STRING) @@ -183,7 +237,8 @@ xf86parseInputClassSection(void) void xf86printInputClassSection (FILE * cf, XF86ConfInputClassPtr ptr) { - char **list; + const xf86MatchGroup *group; + char * const *cur; while (ptr) { fprintf(cf, "Section \"InputClass\"\n"); @@ -193,38 +248,64 @@ xf86printInputClassSection (FILE * cf, XF86ConfInputClassPtr ptr) fprintf(cf, "\tIdentifier \"%s\"\n", ptr->identifier); if (ptr->driver) fprintf(cf, "\tDriver \"%s\"\n", ptr->driver); - if (ptr->match_product) { + + list_for_each_entry(group, &ptr->match_product, entry) { fprintf(cf, "\tMatchProduct \""); - for (list = ptr->match_product; *list; list++) - fprintf(cf, "%s%s", - list == ptr->match_product ? "" : TOKEN_SEP, - *list); + for (cur = group->values; *cur; cur++) + fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP, + *cur); fprintf(cf, "\"\n"); } - if (ptr->match_vendor) { + list_for_each_entry(group, &ptr->match_vendor, entry) { fprintf(cf, "\tMatchVendor \""); - for (list = ptr->match_vendor; *list; list++) - fprintf(cf, "%s%s", - list == ptr->match_vendor ? "" : TOKEN_SEP, - *list); + for (cur = group->values; *cur; cur++) + fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP, + *cur); fprintf(cf, "\"\n"); } - if (ptr->match_device) { + list_for_each_entry(group, &ptr->match_device, entry) { fprintf(cf, "\tMatchDevicePath \""); - for (list = ptr->match_device; *list; list++) - fprintf(cf, "%s%s", - list == ptr->match_device ? "" : TOKEN_SEP, - *list); + for (cur = group->values; *cur; cur++) + fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP, + *cur); + fprintf(cf, "\"\n"); + } + list_for_each_entry(group, &ptr->match_os, entry) { + fprintf(cf, "\tMatchOS \""); + for (cur = group->values; *cur; cur++) + fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP, + *cur); fprintf(cf, "\"\n"); } - if (ptr->match_tag) { - fprintf(cf, "\tMatchTag \""); - for (list = ptr->match_tag; *list; list++) - fprintf(cf, "%s%s", - list == ptr->match_tag ? "" : TOKEN_SEP, - *list); + list_for_each_entry(group, &ptr->match_pnpid, entry) { + fprintf(cf, "\tMatchPnPID \""); + for (cur = group->values; *cur; cur++) + fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP, + *cur); fprintf(cf, "\"\n"); } + list_for_each_entry(group, &ptr->match_usbid, entry) { + fprintf(cf, "\tMatchUSBID \""); + for (cur = group->values; *cur; cur++) + fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP, + *cur); + fprintf(cf, "\"\n"); + } + list_for_each_entry(group, &ptr->match_driver, entry) { + fprintf(cf, "\tMatchDriver \""); + for (cur = group->values; *cur; cur++) + fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP, + *cur); + fprintf(cf, "\"\n"); + } + list_for_each_entry(group, &ptr->match_tag, entry) { + fprintf(cf, "\tMatchTag \""); + for (cur = group->values; *cur; cur++) + fprintf(cf, "%s%s", cur == group->values ? "" : TOKEN_SEP, + *cur); + fprintf(cf, "\"\n"); + } + if (ptr->is_keyboard.set) fprintf(cf, "\tIsKeyboard \"%s\"\n", ptr->is_keyboard.val ? "yes" : "no"); @@ -253,31 +334,63 @@ void xf86freeInputClassList (XF86ConfInputClassPtr ptr) { XF86ConfInputClassPtr prev; - char **list; while (ptr) { + xf86MatchGroup *group, *next; + char **list; + TestFree(ptr->identifier); TestFree(ptr->driver); - if (ptr->match_product) { - for (list = ptr->match_product; *list; list++) + + list_for_each_entry_safe(group, next, &ptr->match_product, entry) { + list_del(&group->entry); + for (list = group->values; *list; list++) free(*list); - free(ptr->match_product); + free(group); } - if (ptr->match_vendor) { - for (list = ptr->match_vendor; *list; list++) + list_for_each_entry_safe(group, next, &ptr->match_vendor, entry) { + list_del(&group->entry); + for (list = group->values; *list; list++) free(*list); - free(ptr->match_vendor); + free(group); } - if (ptr->match_device) { - for (list = ptr->match_device; *list; list++) + list_for_each_entry_safe(group, next, &ptr->match_device, entry) { + list_del(&group->entry); + for (list = group->values; *list; list++) free(*list); - free(ptr->match_device); + free(group); } - if (ptr->match_tag) { - for (list = ptr->match_tag; *list; list++) + list_for_each_entry_safe(group, next, &ptr->match_os, entry) { + list_del(&group->entry); + for (list = group->values; *list; list++) free(*list); - free(ptr->match_tag); + free(group); } + list_for_each_entry_safe(group, next, &ptr->match_pnpid, entry) { + list_del(&group->entry); + for (list = group->values; *list; list++) + free(*list); + free(group); + } + list_for_each_entry_safe(group, next, &ptr->match_usbid, entry) { + list_del(&group->entry); + for (list = group->values; *list; list++) + free(*list); + free(group); + } + list_for_each_entry_safe(group, next, &ptr->match_driver, entry) { + list_del(&group->entry); + for (list = group->values; *list; list++) + free(*list); + free(group); + } + list_for_each_entry_safe(group, next, &ptr->match_tag, entry) { + list_del(&group->entry); + for (list = group->values; *list; list++) + free(*list); + free(group); + } + TestFree(ptr->comment); xf86optionListFree(ptr->option_lst); diff --git a/hw/xfree86/parser/xf86Parser.h b/hw/xfree86/parser/xf86Parser.h index d79544a20..337ad0718 100644 --- a/hw/xfree86/parser/xf86Parser.h +++ b/hw/xfree86/parser/xf86Parser.h @@ -66,6 +66,7 @@ #include <X11/Xdefs.h> #include "xf86Optrec.h" +#include "list.h" #define HAVE_PARSER_DECLS @@ -340,13 +341,24 @@ xf86TriState; typedef struct { + struct list entry; + char **values; +} +xf86MatchGroup; + +typedef struct +{ GenericListRec list; char *identifier; char *driver; - char **match_product; - char **match_vendor; - char **match_device; - char **match_tag; + struct list match_product; + struct list match_vendor; + struct list match_device; + struct list match_os; + struct list match_pnpid; + struct list match_usbid; + struct list match_driver; + struct list match_tag; xf86TriState is_keyboard; xf86TriState is_pointer; xf86TriState is_joystick; diff --git a/hw/xfree86/parser/xf86tokens.h b/hw/xfree86/parser/xf86tokens.h index cb600704b..c16a8f551 100644 --- a/hw/xfree86/parser/xf86tokens.h +++ b/hw/xfree86/parser/xf86tokens.h @@ -279,6 +279,10 @@ typedef enum { MATCH_PRODUCT, MATCH_VENDOR, MATCH_DEVICE_PATH, + MATCH_OS, + MATCH_PNPID, + MATCH_USBID, + MATCH_DRIVER, MATCH_TAG, MATCH_IS_KEYBOARD, MATCH_IS_POINTER, diff --git a/include/dix-config.h.in b/include/dix-config.h.in index 7759aac6a..6a332642b 100644 --- a/include/dix-config.h.in +++ b/include/dix-config.h.in @@ -222,6 +222,9 @@ /* Define to 1 if you have the <sys/types.h> header file. */ #undef HAVE_SYS_TYPES_H +/* Define to 1 if you have the <sys/utsname.h> header file. */ +#undef HAVE_SYS_UTSNAME_H + /* Define to 1 if you have the <sys/vm86.h> header file. */ #undef HAVE_SYS_VM86_H diff --git a/include/input.h b/include/input.h index c68a28441..0a08ea425 100644 --- a/include/input.h +++ b/include/input.h @@ -215,6 +215,8 @@ typedef struct _InputAttributes { char *product; char *vendor; char *device; + char *pnp_id; + char *usb_id; char **tags; /* null-terminated */ uint32_t flags; } InputAttributes; diff --git a/test/input.c b/test/input.c index 12771c59a..b90d3b4fd 100644 --- a/test/input.c +++ b/test/input.c @@ -801,6 +801,20 @@ static void cmp_attr_fields(InputAttributes *attr1, } else g_assert(attr2->device == NULL); + if (attr1->pnp_id != NULL) + { + g_assert(attr1->pnp_id != attr2->pnp_id); + g_assert(strcmp(attr1->pnp_id, attr2->pnp_id) == 0); + } else + g_assert(attr2->pnp_id == NULL); + + if (attr1->usb_id != NULL) + { + g_assert(attr1->usb_id != attr2->usb_id); + g_assert(strcmp(attr1->usb_id, attr2->usb_id) == 0); + } else + g_assert(attr2->usb_id == NULL); + tags1 = attr1->tags; tags2 = attr2->tags; @@ -866,6 +880,16 @@ static void dix_input_attributes(void) cmp_attr_fields(&orig, new); FreeInputAttributes(new); + orig.pnp_id = "PnPID"; + new = DuplicateInputAttributes(&orig); + cmp_attr_fields(&orig, new); + FreeInputAttributes(new); + + orig.usb_id = "USBID"; + new = DuplicateInputAttributes(&orig); + cmp_attr_fields(&orig, new); + FreeInputAttributes(new); + orig.flags = 0xF0; new = DuplicateInputAttributes(&orig); cmp_attr_fields(&orig, new); |