Commit a71bdff4 authored by Dan Nicholson's avatar Dan Nicholson Committed by Peter Hutterer

xfree86: Allow multiple InputClass Match* entries for && matching

Currently when there multiple InputClass entries of the same type, only
the last entry is used and the previous ones are ignored. Instead,
multiple entries are used to create multiple matching conditions.

For instance, an InputClass with

	MatchProduct "foo"
	MatchProduct "bar"

will require that the device's product name contain both foo and bar.
This provides a complement to the || style matching when an entry is
split using the "|" token.

The xorg.conf man page has added an example to hopefully clarify the two
types of compound matches.
Signed-off-by: Dan Nicholson's avatarDan Nicholson <dbn.lists@gmail.com>
Reviewed-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent 84262592
......@@ -551,29 +551,42 @@ match_path_pattern(const char *attr, const char *pattern)
#endif
/*
* Match an attribute against a NULL terminated list of patterns. If any
* pattern is matched, return TRUE.
* 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, char **patterns,
MatchAttrToken(const char *attr, struct list *patterns,
int (*compare)(const char *attr, const char *pattern))
{
char **cur;
const xf86MatchGroup *group;
/* If there are no patterns, accept the match */
if (!patterns)
if (list_is_empty(patterns))
return TRUE;
/* If there are patterns but no attribute, reject the match */
if (!attr)
return FALSE;
/* Otherwise, iterate the patterns looking for a match */
for (cur = patterns; *cur; cur++)
if ((*compare)(attr, *cur) == 0)
return TRUE;
/*
* 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;
}
return FALSE;
/* All the entries in the list matched the attribute */
return TRUE;
}
/*
......@@ -585,41 +598,41 @@ InputClassMatches(const XF86ConfInputClassPtr iclass,
const InputAttributes *attrs)
{
/* MatchProduct substring */
if (!MatchAttrToken(attrs->product, iclass->match_product, match_substring))
if (!MatchAttrToken(attrs->product, &iclass->match_product, match_substring))
return FALSE;
/* MatchVendor substring */
if (!MatchAttrToken(attrs->vendor, iclass->match_vendor, match_substring))
if (!MatchAttrToken(attrs->vendor, &iclass->match_vendor, match_substring))
return FALSE;
/* MatchDevicePath pattern */
if (!MatchAttrToken(attrs->device, iclass->match_device, match_path_pattern))
if (!MatchAttrToken(attrs->device, &iclass->match_device, match_path_pattern))
return FALSE;
/* MatchOS case-insensitive string */
if (!MatchAttrToken(HostOS(), iclass->match_os, strcasecmp))
if (!MatchAttrToken(HostOS(), &iclass->match_os, strcasecmp))
return FALSE;
/* MatchPnPID pattern */
if (!MatchAttrToken(attrs->pnp_id, iclass->match_pnpid, match_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))
if (!MatchAttrToken(attrs->usb_id, &iclass->match_usbid, match_pattern))
return FALSE;
/*
* MatchTag string
* See if any of the device's tags match any of the MatchTag tokens.
*/
if (iclass->match_tag) {
if (!list_is_empty(&iclass->match_tag)) {
char * const *tag;
Bool match;
if (!attrs->tags)
return FALSE;
for (tag = attrs->tags, match = FALSE; *tag; tag++) {
if (MatchAttrToken(*tag, iclass->match_tag, strcmp)) {
if (MatchAttrToken(*tag, &iclass->match_tag, strcmp)) {
match = TRUE;
break;
}
......
......@@ -1065,26 +1065,42 @@ 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
......@@ -1092,14 +1108,12 @@ case-insensitive
.RI \*q matchos \*q
string. This entry is only supported on platforms providing the
.BR uname (2)
system call. Multiple operating systems can be matched by separating arguments
with a '|' character.
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. Multiple IDs can be matched by separating arguments
with a '|' character.
shell wildcard pattern.
.TP 7
.BI "MatchUSBID \*q" matchusb \*q
The device's USB ID can be checked against the
......@@ -1107,17 +1121,20 @@ The device's USB ID can be checked against the
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. Multiple IDs can be matched by separating arguments with a '|'
character.
program.
.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
......@@ -1130,9 +1147,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
......
......@@ -64,6 +64,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)
{
......@@ -72,6 +84,15 @@ 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_tag);
while ((token = xf86getToken(InputClassTab)) != ENDSECTION) {
switch (token) {
case COMMENT:
......@@ -99,37 +120,44 @@ 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");
ptr->match_os = xstrtokenize(val.str, TOKEN_SEP);
add_group_entry(&ptr->match_os,
xstrtokenize(val.str, TOKEN_SEP));
break;
case MATCH_PNPID:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "MatchPnPID");
ptr->match_pnpid = xstrtokenize(val.str, TOKEN_SEP);
add_group_entry(&ptr->match_pnpid,
xstrtokenize(val.str, TOKEN_SEP));
break;
case MATCH_USBID:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "MatchUSBID");
ptr->match_usbid = xstrtokenize(val.str, TOKEN_SEP);
add_group_entry(&ptr->match_usbid,
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)
......@@ -201,7 +229,8 @@ xf86parseInputClassSection(void)
void
xf86printInputClassSection (FILE * cf, XF86ConfInputClassPtr ptr)
{
char **list;
const xf86MatchGroup *group;
char * const *cur;
while (ptr) {
fprintf(cf, "Section \"InputClass\"\n");
......@@ -211,62 +240,57 @@ 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");
}
if (ptr->match_os) {
list_for_each_entry(group, &ptr->match_os, entry) {
fprintf(cf, "\tMatchOS \"");
for (list = ptr->match_os; *list; list++)
fprintf(cf, "%s%s",
list == ptr->match_os ? "" : 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_pnpid) {
list_for_each_entry(group, &ptr->match_pnpid, entry) {
fprintf(cf, "\tMatchPnPID \"");
for (list = ptr->match_pnpid; *list; list++)
fprintf(cf, "%s%s",
list == ptr->match_pnpid ? "" : 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_usbid) {
list_for_each_entry(group, &ptr->match_usbid, entry) {
fprintf(cf, "\tMatchUSBID \"");
for (list = ptr->match_usbid; *list; list++)
fprintf(cf, "%s%s",
list == ptr->match_usbid ? "" : 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_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_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");
......@@ -295,46 +319,57 @@ 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_os) {
for (list = ptr->match_os; *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_os);
free(group);
}
if (ptr->match_pnpid) {
for (list = ptr->match_pnpid; *list; list++)
list_for_each_entry_safe(group, next, &ptr->match_pnpid, entry) {
list_del(&group->entry);
for (list = group->values; *list; list++)
free(*list);
free(ptr->match_pnpid);
free(group);
}
if (ptr->match_usbid) {
for (list = ptr->match_usbid; *list; list++)
list_for_each_entry_safe(group, next, &ptr->match_usbid, entry) {
list_del(&group->entry);
for (list = group->values; *list; list++)
free(*list);
free(ptr->match_usbid);
free(group);
}
if (ptr->match_tag) {
for (list = ptr->match_tag; *list; list++)
list_for_each_entry_safe(group, next, &ptr->match_tag, entry) {
list_del(&group->entry);
for (list = group->values; *list; list++)
free(*list);
free(ptr->match_tag);
free(group);
}
TestFree(ptr->comment);
xf86optionListFree(ptr->option_lst);
......
......@@ -66,6 +66,7 @@
#include <X11/Xdefs.h>
#include "xf86Optrec.h"
#include "list.h"
#define HAVE_PARSER_DECLS
......@@ -338,18 +339,25 @@ typedef struct
}
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_os;
char **match_pnpid;
char **match_usbid;
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_tag;
xf86TriState is_keyboard;
xf86TriState is_pointer;
xf86TriState is_joystick;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment