[PATCH v2 3/3] xfree86: Introduce InputClass configuration

Dan Nicholson dbn.lists at gmail.com
Fri Nov 27 14:01:01 PST 2009


Currently Xorg uses hal's fdi files to decide what configuration options
are applied to automatically added input devices. This is sub-optimal
since it requires users to use a new and different configuration store
than xorg.conf.

The InputClass section attempts to provide a system similar to hal where
configuration can be applied to all devices with certain attributes. For
now, devices can be matched to a substring of the product name via a
MatchName entry, to a pathname pattern of the device file via a
MatchDevicePath entry, or against boolean MatchIsKeyboard/MatchIsPointer
entries.

See the INPUTCLASS section in xorg.conf(5) for more details.

Signed-off-by: Dan Nicholson <dbn.lists at gmail.com>
---
 configure.ac                         |    2 +-
 hw/xfree86/common/xf86Xinput.c       |   78 ++++++++++++++
 hw/xfree86/doc/man/xorg.conf.man.pre |   89 ++++++++++++++++
 hw/xfree86/parser/Configint.h        |    2 +
 hw/xfree86/parser/InputClass.c       |  191 ++++++++++++++++++++++++++++++++++
 hw/xfree86/parser/Makefile.am        |    1 +
 hw/xfree86/parser/configProcs.h      |    5 +
 hw/xfree86/parser/read.c             |    8 ++
 hw/xfree86/parser/write.c            |    2 +
 hw/xfree86/parser/xf86Parser.h       |   15 +++
 hw/xfree86/parser/xf86tokens.h       |    8 ++-
 include/dix-config.h.in              |    3 +
 12 files changed, 402 insertions(+), 2 deletions(-)
 create mode 100644 hw/xfree86/parser/InputClass.c

diff --git a/configure.ac b/configure.ac
index fcd8875..eba486d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -122,7 +122,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])
+AC_CHECK_HEADERS([fcntl.h stdlib.h string.h unistd.h dlfcn.h stropts.h fnmatch.h])
 
 dnl Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c
index 11b7315..40ee629 100644
--- a/hw/xfree86/common/xf86Xinput.c
+++ b/hw/xfree86/common/xf86Xinput.c
@@ -57,9 +57,11 @@
 #include <X11/Xatom.h>
 #include "xf86.h"
 #include "xf86Priv.h"
+#include "xf86Config.h"
 #include "xf86Xinput.h"
 #include "XIstubs.h"
 #include "xf86Optrec.h"
+#include "xf86Parser.h"
 #include "mipointer.h"
 #include "xf86InPriv.h"
 #include "compiler.h"
@@ -74,6 +76,11 @@
 #include "exglobals.h"
 #include "eventstr.h"
 
+#include <string.h>     /* InputClassMatches */
+#ifdef HAVE_FNMATCH_H
+#include <fnmatch.h>
+#endif
+
 #include "extnsionst.h"
 
 #include "windowstr.h"	/* screenIsSaved */
@@ -466,6 +473,70 @@ AddOtherInputDevices(void)
 {
 }
 
+/*
+ * Classes without any Match statements match all devices. Otherwise, all
+ * statements must match.
+ */
+static Bool
+InputClassMatches(XF86ConfInputClassPtr iclass, InputAttributes *attrs)
+{
+    if (iclass->inc_match_name &&
+        (!attrs->name || !strstr(attrs->name, iclass->inc_match_name)))
+        return False;
+    if (iclass->inc_match_device &&
+#ifdef HAVE_FNMATCH_H
+        (!attrs->device ||
+         fnmatch(iclass->inc_match_device, attrs->device, 0) != 0))
+#else
+        (!attrs->device || !strstr(attrs->device, iclass->inc_match_device)))
+#endif
+        return False;
+    if (iclass->inc_is_keyboard.set &&
+        iclass->inc_is_keyboard.val != !!(attrs->flags & ATTR_KEYBOARD))
+        return False;
+    if (iclass->inc_is_pointer.set &&
+        iclass->inc_is_pointer.val != !!(attrs->flags & ATTR_POINTER))
+        return False;
+    return True;
+}
+
+/*
+ * Merge in any InputClass configurations. Each InputClass section can
+ * add to the original device configuration as well as any previous
+ * InputClass sections.
+ */
+static int
+MergeInputClasses(IDevPtr idev, InputAttributes *attrs)
+{
+    XF86ConfInputClassPtr cl;
+    XF86OptionPtr classopts;
+
+    for (cl = xf86configptr->conf_inputclass_lst; cl; cl = cl->list.next) {
+        if (!InputClassMatches(cl, attrs))
+            continue;
+
+        xf86Msg(X_CONFIG, "%s: Applying InputClass \"%s\"\n",
+                idev->identifier, cl->inc_identifier);
+        if (cl->inc_driver && !idev->driver) {
+            idev->driver = xstrdup(cl->inc_driver);
+            if (!idev->driver) {
+                xf86Msg(X_ERROR, "Could not allocate memory while merging "
+                        "InputClass configuration");
+                return BadAlloc;
+            }
+        }
+
+        classopts = xf86optionListDup(cl->inc_option_lst);
+        if (idev->commonOptions)
+            idev->commonOptions = xf86optionListMerge(classopts,
+                                                      idev->commonOptions);
+        else
+            idev->commonOptions = classopts;
+    }
+
+    return Success;
+}
+
 /**
  * Create a new input device, activate and enable it.
  *
@@ -636,6 +707,13 @@ NewInputDeviceRequest (InputOption *options, InputAttributes *attrs,
         option->value = NULL;
     }
 
+    /* Apply InputClass settings */
+    if (attrs) {
+        rval = MergeInputClasses(idev, attrs);
+        if (rval != Success)
+            goto unwind;
+    }
+
     rval = xf86NewInputDevice(idev, pdev,
                 (!is_auto || (is_auto && xf86Info.autoEnableDevices)));
     if (rval == Success)
diff --git a/hw/xfree86/doc/man/xorg.conf.man.pre b/hw/xfree86/doc/man/xorg.conf.man.pre
index ace041c..fe0ade8 100644
--- a/hw/xfree86/doc/man/xorg.conf.man.pre
+++ b/hw/xfree86/doc/man/xorg.conf.man.pre
@@ -117,6 +117,7 @@ The section names are:
 .BR "Module         " "Dynamic module loading"
 .BR "Extensions     " "Extension enabling"
 .BR "InputDevice    " "Input device description"
+.BR "InputClass     " "Input class description"
 .BR "Device         " "Graphics device description"
 .BR "VideoAdaptor   " "Xv video adaptor description"
 .BR "Monitor        " "Monitor description"
@@ -889,6 +890,94 @@ Default: 0.
 .TP 7
 .BI "Option \*qSendDragEvents\*q  \*q" boolean \*q
 ???
+.SH "INPUTCLASS SECTION"
+The config file may have multiple
+.B InputClass
+sections.
+These sections are optional and are used to provide configuration for a
+class of input devices as they are automatically added. An input device can
+match more than one
+.B InputClass
+section. Each class can only supplement settings from a previous class, so
+it is best to arrange the sections with the most generic matches last.
+.PP
+.B InputClass
+sections have the following format:
+.PP
+.RS 4
+.nf
+.B  "Section \*qInputClass\*q"
+.BI "    Identifier  \*q" name \*q
+.BI "    Driver      \*q" inputdriver \*q
+.I  "    entries"
+.I  "    ..."
+.I  "    options"
+.I  "    ..."
+.B  "EndSection"
+.fi
+.RE
+.PP
+The
+.B Identifier
+and
+.B Driver
+entries are required in all
+.B InputClass
+sections.
+All other entries are optional.
+.PP
+The
+.B Identifier
+entry specifies the unique name for this input class.
+The
+.B Driver
+entry specifies the name of the driver to use for this input device.
+After all classes have been examined, the
+.RI \*q inputdriver \*q
+module from the final
+.B Driver
+entry will be enabled when using the loadable server.
+.PP
+When an input device is automatically added, its characteristics are
+checked against all
+.B InputClass
+sections. Each section can contain optional entries to narrow the match
+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
+.TP 7
+.BI "MatchName  \*q" matchname \*q
+This entry can be used to check if the substring
+.RI \*q matchname \*q
+occurs in the device's product 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.
+.TP 7
+.BI "MatchIsKeyboard    \*q" iskeyboard \*q
+Match keyboard devices. This entry takes a boolean argument similar to
+.B Option
+entries.
+.TP 7
+.BI "MatchIsPointer     \*q" ispointer \*q
+Match pointer devices. This entry takes a boolean argument similar to
+.B Option
+entries.
+.PP
+When an input device has been matched to the
+.B InputClass
+section, any
+.B Option
+entries are applied to the device. See the
+.B InputDevice
+section above for a description of the various
+.B Option
+entries.
 .SH "DEVICE SECTION"
 The config file may have multiple
 .B Device
diff --git a/hw/xfree86/parser/Configint.h b/hw/xfree86/parser/Configint.h
index 03509b3..80b5f00 100644
--- a/hw/xfree86/parser/Configint.h
+++ b/hw/xfree86/parser/Configint.h
@@ -203,6 +203,8 @@ else\
 "This section must have only one of either %s line."
 #define UNDEFINED_INPUTDRIVER_MSG \
 "InputDevice section \"%s\" must have a Driver line."
+#define UNDEFINED_INPUTCLASSDRIVER_MSG \
+"InputClass section \"%s\" must have a Driver line."
 #define INVALID_GAMMA_MSG \
 "gamma correction value(s) expected\n either one value or three r/g/b values."
 #define GROUP_MSG \
diff --git a/hw/xfree86/parser/InputClass.c b/hw/xfree86/parser/InputClass.c
new file mode 100644
index 0000000..54e7039
--- /dev/null
+++ b/hw/xfree86/parser/InputClass.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2009 Dan Nicholson
+ *
+ * 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 furnished 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, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* View/edit this file with tab stops set to 4 */
+
+#ifdef HAVE_XORG_CONFIG_H
+#include <xorg-config.h>
+#endif
+
+#include "xf86Parser.h"
+#include "xf86tokens.h"
+#include "Configint.h"
+
+extern LexRec val;
+
+static
+xf86ConfigSymTabRec InputClassTab[] =
+{
+    {ENDSECTION, "endsection"},
+    {IDENTIFIER, "identifier"},
+    {OPTION, "option"},
+    {DRIVER, "driver"},
+    {MATCH_NAME, "matchname"},
+    {MATCH_DEVICE_PATH, "matchdevicepath"},
+    {MATCH_IS_KEYBOARD, "matchiskeyboard"},
+    {MATCH_IS_POINTER, "matchispointer"},
+    {-1, ""},
+};
+
+#define CLEANUP xf86freeInputClassList
+
+XF86ConfInputClassPtr
+xf86parseInputClassSection(void)
+{
+    int has_ident = FALSE;
+    int token;
+
+    parsePrologue(XF86ConfInputClassPtr, XF86ConfInputClassRec)
+
+    while ((token = xf86getToken(InputClassTab)) != ENDSECTION) {
+        switch (token) {
+        case COMMENT:
+            ptr->inc_comment = xf86addComment(ptr->inc_comment, val.str);
+            break;
+        case IDENTIFIER:
+            if (xf86getSubToken(&(ptr->inc_comment)) != STRING)
+                Error(QUOTE_MSG, "Identifier");
+            if (has_ident == TRUE)
+                Error(MULTIPLE_MSG, "Identifier");
+            ptr->inc_identifier = val.str;
+            has_ident = TRUE;
+            break;
+        case DRIVER:
+            if (xf86getSubToken(&(ptr->inc_comment)) != STRING)
+                Error(QUOTE_MSG, "Driver");
+            if (strcmp(val.str, "keyboard") == 0)
+                ptr->inc_driver = "kbd";
+            else
+                ptr->inc_driver = val.str;
+            break;
+        case OPTION:
+            ptr->inc_option_lst = xf86parseOption(ptr->inc_option_lst);
+            break;
+        case MATCH_NAME:
+            if (xf86getSubToken(&(ptr->inc_comment)) != STRING)
+                Error(QUOTE_MSG, "MatchName");
+            ptr->inc_match_name = val.str;
+            break;
+        case MATCH_DEVICE_PATH:
+            if (xf86getSubToken(&(ptr->inc_comment)) != STRING)
+                Error(QUOTE_MSG, "MatchDevicePath");
+            ptr->inc_match_device = val.str;
+            break;
+        case MATCH_IS_KEYBOARD:
+            if (xf86getSubToken(&(ptr->inc_comment)) != STRING)
+                Error(QUOTE_MSG, "MatchIsKeyboard");
+            ptr->inc_is_keyboard.set = xf86getBoolValue(&ptr->inc_is_keyboard.val,
+                                                        val.str);
+            if (!ptr->inc_is_keyboard.set)
+                Error(BOOL_MSG, "MatchIsKeyboard");
+            break;
+        case MATCH_IS_POINTER:
+            if (xf86getSubToken(&(ptr->inc_comment)) != STRING)
+                Error(QUOTE_MSG, "MatchIsPointer");
+            ptr->inc_is_pointer.set = xf86getBoolValue(&ptr->inc_is_pointer.val,
+                                                       val.str);
+            if (!ptr->inc_is_pointer.set)
+                Error(BOOL_MSG, "MatchIsPointer");
+            break;
+        case EOF_TOKEN:
+            Error(UNEXPECTED_EOF_MSG, NULL);
+            break;
+        default:
+            Error(INVALID_KEYWORD_MSG, xf86tokenString ());
+            break;
+        }
+    }
+
+    if (!has_ident)
+        Error(NO_IDENT_MSG, NULL);
+
+#ifdef DEBUG
+    printf("InputClass section parsed\n");
+#endif
+
+    return ptr;
+}
+
+void
+xf86printInputClassSection (FILE * cf, XF86ConfInputClassPtr ptr)
+{
+    while (ptr) {
+        fprintf(cf, "Section \"InputClass\"\n");
+        if (ptr->inc_comment)
+            fprintf(cf, "%s", ptr->inc_comment);
+        if (ptr->inc_identifier)
+            fprintf(cf, "\tIdentifier    \"%s\"\n", ptr->inc_identifier);
+        if (ptr->inc_driver)
+            fprintf(cf, "\tDriver        \"%s\"\n", ptr->inc_driver);
+        if (ptr->inc_match_name)
+            fprintf(cf, "\tMatchName  \"%s\"\n", ptr->inc_match_name);
+        if (ptr->inc_match_device)
+            fprintf(cf, "\tMatchDevicePath \"%s\"\n", ptr->inc_match_device);
+        if (ptr->inc_is_keyboard.set)
+            fprintf(cf, "\tIsKeyboard    \"%s\"\n",
+                    ptr->inc_is_keyboard.val ? "yes" : "no");
+        if (ptr->inc_is_pointer.set)
+            fprintf(cf, "\tIsPointer     \"%s\"\n",
+                    ptr->inc_is_pointer.val ? "yes" : "no");
+        xf86printOptionList(cf, ptr->inc_option_lst, 1);
+        fprintf(cf, "EndSection\n\n");
+        ptr = ptr->list.next;
+    }
+}
+
+void
+xf86freeInputClassList (XF86ConfInputClassPtr ptr)
+{
+    XF86ConfInputClassPtr prev;
+
+    while (ptr) {
+        TestFree(ptr->inc_identifier);
+        TestFree(ptr->inc_driver);
+        TestFree(ptr->inc_match_name);
+        TestFree(ptr->inc_match_device);
+        TestFree(ptr->inc_comment);
+        xf86optionListFree(ptr->inc_option_lst);
+
+        prev = ptr;
+        ptr = ptr->list.next;
+        free(prev);
+    }
+}
+
+int
+xf86validateInputClass (XF86ConfigPtr p)
+{
+    XF86ConfInputClassPtr input = p->conf_inputclass_lst;
+
+    while (input) {
+        if (!input->inc_driver) {
+            xf86validationError(UNDEFINED_INPUTCLASSDRIVER_MSG,
+                                input->inc_identifier);
+            return FALSE;
+        }
+        input = input->list.next;
+    }
+    return TRUE;
+}
diff --git a/hw/xfree86/parser/Makefile.am b/hw/xfree86/parser/Makefile.am
index b8fab28..49c191f 100644
--- a/hw/xfree86/parser/Makefile.am
+++ b/hw/xfree86/parser/Makefile.am
@@ -13,6 +13,7 @@ INTERNAL_SOURCES= \
 	Files.c \
 	Flags.c \
 	Input.c \
+	InputClass.c \
 	Layout.c \
 	Module.c \
 	Video.c \
diff --git a/hw/xfree86/parser/configProcs.h b/hw/xfree86/parser/configProcs.h
index 26ba40e..9c50ea0 100644
--- a/hw/xfree86/parser/configProcs.h
+++ b/hw/xfree86/parser/configProcs.h
@@ -48,6 +48,11 @@ XF86ConfInputPtr xf86parseInputSection(void);
 void xf86printInputSection(FILE *f, XF86ConfInputPtr ptr);
 void xf86freeInputList(XF86ConfInputPtr ptr);
 int xf86validateInput (XF86ConfigPtr p);
+/* InputClass.c */
+XF86ConfInputClassPtr xf86parseInputClassSection(void);
+void xf86printInputClassSection(FILE *f, XF86ConfInputClassPtr ptr);
+void xf86freeInputClassList(XF86ConfInputClassPtr ptr);
+int xf86validateInputClass(XF86ConfigPtr p);
 /* Layout.c */
 XF86ConfLayoutPtr xf86parseLayoutSection(void);
 void xf86printLayoutSection(FILE *cf, XF86ConfLayoutPtr ptr);
diff --git a/hw/xfree86/parser/read.c b/hw/xfree86/parser/read.c
index e965d20..1091be5 100644
--- a/hw/xfree86/parser/read.c
+++ b/hw/xfree86/parser/read.c
@@ -177,6 +177,14 @@ xf86readConfigFile (void)
 				HANDLE_LIST (conf_input_lst, xf86parseInputSection,
 							 XF86ConfInputPtr);
 			}
+			else if (xf86nameCompare(val.str, "inputclass") == 0)
+			{
+				free(val.str);
+				val.str = NULL;
+				HANDLE_LIST (conf_inputclass_lst,
+						xf86parseInputClassSection,
+						XF86ConfInputClassPtr);
+			}
 			else if (xf86nameCompare (val.str, "module") == 0)
 			{
 				free(val.str);
diff --git a/hw/xfree86/parser/write.c b/hw/xfree86/parser/write.c
index 3b77b93..083203c 100644
--- a/hw/xfree86/parser/write.c
+++ b/hw/xfree86/parser/write.c
@@ -117,6 +117,8 @@ doWriteConfigFile (const char *filename, XF86ConfigPtr cptr)
 
 	xf86printInputSection (cf, cptr->conf_input_lst);
 
+	xf86printInputClassSection (cf, cptr->conf_inputclass_lst);
+
 	xf86printVideoAdaptorSection (cf, cptr->conf_videoadaptor_lst);
 
 	xf86printModesSection (cf, cptr->conf_modes_lst);
diff --git a/hw/xfree86/parser/xf86Parser.h b/hw/xfree86/parser/xf86Parser.h
index 7b445e1..135db0b 100644
--- a/hw/xfree86/parser/xf86Parser.h
+++ b/hw/xfree86/parser/xf86Parser.h
@@ -338,6 +338,20 @@ typedef struct
 }
 xf86Bool;
 
+typedef struct
+{
+	GenericListRec list;
+	char *inc_identifier;
+	char *inc_driver;
+	char *inc_match_name;
+	char *inc_match_device;
+	xf86Bool inc_is_keyboard;
+	xf86Bool inc_is_pointer;
+	XF86OptionPtr inc_option_lst;
+	char *inc_comment;
+}
+XF86ConfInputClassRec, *XF86ConfInputClassPtr;
+
 /* Values for adj_where */
 #define CONF_ADJ_OBSOLETE	-1
 #define CONF_ADJ_ABSOLUTE	0
@@ -446,6 +460,7 @@ typedef struct
 	XF86ConfDevicePtr conf_device_lst;
 	XF86ConfScreenPtr conf_screen_lst;
 	XF86ConfInputPtr conf_input_lst;
+	XF86ConfInputClassPtr conf_inputclass_lst;
 	XF86ConfLayoutPtr conf_layout_lst;
 	XF86ConfVendorPtr conf_vendor_lst;
 	XF86ConfDRIPtr conf_dri;
diff --git a/hw/xfree86/parser/xf86tokens.h b/hw/xfree86/parser/xf86tokens.h
index 4c1d38c..f1833f5 100644
--- a/hw/xfree86/parser/xf86tokens.h
+++ b/hw/xfree86/parser/xf86tokens.h
@@ -273,7 +273,13 @@ typedef enum {
 
     /* DRI Tokens */
     GROUP,
-    BUFFERS
+    BUFFERS,
+
+    /* InputClass Tokens */
+    MATCH_NAME,
+    MATCH_DEVICE_PATH,
+    MATCH_IS_KEYBOARD,
+    MATCH_IS_POINTER
 } ParserTokens;
 
 #endif /* _xf86_tokens_h */
diff --git a/include/dix-config.h.in b/include/dix-config.h.in
index 1096371..ab04414 100644
--- a/include/dix-config.h.in
+++ b/include/dix-config.h.in
@@ -231,6 +231,9 @@
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 
+/* Define to 1 if you have the <fnmatch.h> header file. */
+#undef HAVE_FNMATCH_H
+
 /* Have /dev/urandom */
 #undef HAVE_URANDOM
 
-- 
1.6.2.5



More information about the xorg-devel mailing list