xfree86: Introduce InputClass configuration

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 MatchProduct entry
* A substring of the vendir name via a MatchVendor entry
* A pathname pattern of the device file via a MatchDevicePath entry
* A device type via boolean entries for MatchIsKeyboard, MatchIsPointer,
  MatchIsJoystick, MatchIsTablet, MatchIsTouchpad and MatchIsTouchscreen

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

Signed-off-by: Dan Nicholson <dbn.lists@gmail.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Dan Nicholson 2009-10-02 06:29:28 -07:00
parent 0711598dd3
commit 42e8c9224e
11 changed files with 472 additions and 2 deletions

View File

@ -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

View File

@ -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,85 @@ AddOtherInputDevices(void)
{
}
/*
* Classes without any Match statements match all devices. Otherwise, all
* statements must match.
*/
static Bool
InputClassMatches(XF86ConfInputClassPtr iclass, InputAttributes *attrs)
{
if (iclass->match_product &&
(!attrs->product || !strstr(attrs->product, iclass->match_product)))
return False;
if (iclass->match_vendor &&
(!attrs->vendor || !strstr(attrs->vendor, iclass->match_vendor)))
return False;
if (iclass->match_device &&
#ifdef HAVE_FNMATCH_H
(!attrs->device ||
fnmatch(iclass->match_device, attrs->device, 0) != 0))
#else
(!attrs->device || !strstr(attrs->device, iclass->match_device)))
#endif
return False;
if (iclass->is_keyboard.set &&
iclass->is_keyboard.val != !!(attrs->flags & ATTR_KEYBOARD))
return False;
if (iclass->is_pointer.set &&
iclass->is_pointer.val != !!(attrs->flags & ATTR_POINTER))
return False;
if (iclass->is_joystick.set &&
iclass->is_joystick.val != !!(attrs->flags & ATTR_JOYSTICK))
return False;
if (iclass->is_tablet.set &&
iclass->is_tablet.val != !!(attrs->flags & ATTR_TABLET))
return False;
if (iclass->is_touchpad.set &&
iclass->is_touchpad.val != !!(attrs->flags & ATTR_TOUCHPAD))
return False;
if (iclass->is_touchscreen.set &&
iclass->is_touchscreen.val != !!(attrs->flags & ATTR_TOUCHSCREEN))
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->identifier);
if (cl->driver && !idev->driver) {
idev->driver = xstrdup(cl->driver);
if (!idev->driver) {
xf86Msg(X_ERROR, "Could not allocate memory while merging "
"InputClass configuration");
return BadAlloc;
}
}
classopts = xf86optionListDup(cl->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 +722,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)

View File

@ -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"
@ -882,6 +883,101 @@ may be reattached or set floating at runtime.
.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
.I " entries"
.I " ..."
.I " options"
.I " ..."
.B "EndSection"
.fi
.RE
.PP
The
.B Identifier
entry is 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 "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.
.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.
.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" bool \*q
.TP 7
.BI "MatchIsPointer \*q" bool \*q
.TP 7
.BI "MatchIsJoystick \*q" bool \*q
.TP 7
.BI "MatchIsTablet \*q" bool \*q
.TP 7
.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
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

View File

@ -0,0 +1,232 @@
/*
* 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_PRODUCT, "matchproduct"},
{MATCH_VENDOR, "matchvendor"},
{MATCH_DEVICE_PATH, "matchdevicepath"},
{MATCH_IS_KEYBOARD, "matchiskeyboard"},
{MATCH_IS_POINTER, "matchispointer"},
{MATCH_IS_JOYSTICK, "matchisjoystick"},
{MATCH_IS_TABLET, "matchistablet"},
{MATCH_IS_TOUCHPAD, "matchistouchpad"},
{MATCH_IS_TOUCHSCREEN, "matchistouchscreen"},
{-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->comment = xf86addComment(ptr->comment, val.str);
break;
case IDENTIFIER:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "Identifier");
if (has_ident == TRUE)
Error(MULTIPLE_MSG, "Identifier");
ptr->identifier = val.str;
has_ident = TRUE;
break;
case DRIVER:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "Driver");
if (strcmp(val.str, "keyboard") == 0)
ptr->driver = "kbd";
else
ptr->driver = val.str;
break;
case OPTION:
ptr->option_lst = xf86parseOption(ptr->option_lst);
break;
case MATCH_PRODUCT:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "MatchProduct");
ptr->match_product = val.str;
break;
case MATCH_VENDOR:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "MatchVendor");
ptr->match_vendor = val.str;
break;
case MATCH_DEVICE_PATH:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "MatchDevicePath");
ptr->match_device = val.str;
break;
case MATCH_IS_KEYBOARD:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "MatchIsKeyboard");
ptr->is_keyboard.set = xf86getBoolValue(&ptr->is_keyboard.val,
val.str);
if (!ptr->is_keyboard.set)
Error(BOOL_MSG, "MatchIsKeyboard");
break;
case MATCH_IS_POINTER:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "MatchIsPointer");
ptr->is_pointer.set = xf86getBoolValue(&ptr->is_pointer.val,
val.str);
if (!ptr->is_pointer.set)
Error(BOOL_MSG, "MatchIsPointer");
break;
case MATCH_IS_JOYSTICK:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "MatchIsJoystick");
ptr->is_joystick.set = xf86getBoolValue(&ptr->is_joystick.val,
val.str);
if (!ptr->is_joystick.set)
Error(BOOL_MSG, "MatchIsJoystick");
break;
case MATCH_IS_TABLET:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "MatchIsTablet");
ptr->is_tablet.set = xf86getBoolValue(&ptr->is_tablet.val,
val.str);
if (!ptr->is_tablet.set)
Error(BOOL_MSG, "MatchIsTablet");
break;
case MATCH_IS_TOUCHPAD:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "MatchIsTouchpad");
ptr->is_touchpad.set = xf86getBoolValue(&ptr->is_touchpad.val,
val.str);
if (!ptr->is_touchpad.set)
Error(BOOL_MSG, "MatchIsTouchpad");
break;
case MATCH_IS_TOUCHSCREEN:
if (xf86getSubToken(&(ptr->comment)) != STRING)
Error(QUOTE_MSG, "MatchIsTouchscreen");
ptr->is_touchscreen.set = xf86getBoolValue(&ptr->is_touchscreen.val,
val.str);
if (!ptr->is_touchscreen.set)
Error(BOOL_MSG, "MatchIsTouchscreen");
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->comment)
fprintf(cf, "%s", ptr->comment);
if (ptr->identifier)
fprintf(cf, "\tIdentifier \"%s\"\n", ptr->identifier);
if (ptr->driver)
fprintf(cf, "\tDriver \"%s\"\n", ptr->driver);
if (ptr->match_product)
fprintf(cf, "\tMatchProduct \"%s\"\n", ptr->match_product);
if (ptr->match_vendor)
fprintf(cf, "\tMatchVendor \"%s\"\n", ptr->match_vendor);
if (ptr->match_device)
fprintf(cf, "\tMatchDevicePath \"%s\"\n", ptr->match_device);
if (ptr->is_keyboard.set)
fprintf(cf, "\tIsKeyboard \"%s\"\n",
ptr->is_keyboard.val ? "yes" : "no");
if (ptr->is_pointer.set)
fprintf(cf, "\tIsPointer \"%s\"\n",
ptr->is_pointer.val ? "yes" : "no");
if (ptr->is_joystick.set)
fprintf(cf, "\tIsJoystick \"%s\"\n",
ptr->is_joystick.val ? "yes" : "no");
if (ptr->is_tablet.set)
fprintf(cf, "\tIsTablet \"%s\"\n",
ptr->is_tablet.val ? "yes" : "no");
if (ptr->is_touchpad.set)
fprintf(cf, "\tIsTouchpad \"%s\"\n",
ptr->is_touchpad.val ? "yes" : "no");
if (ptr->is_touchscreen.set)
fprintf(cf, "\tIsTouchscreen \"%s\"\n",
ptr->is_touchscreen.val ? "yes" : "no");
xf86printOptionList(cf, ptr->option_lst, 1);
fprintf(cf, "EndSection\n\n");
ptr = ptr->list.next;
}
}
void
xf86freeInputClassList (XF86ConfInputClassPtr ptr)
{
XF86ConfInputClassPtr prev;
while (ptr) {
TestFree(ptr->identifier);
TestFree(ptr->driver);
TestFree(ptr->match_product);
TestFree(ptr->match_vendor);
TestFree(ptr->match_device);
TestFree(ptr->comment);
xf86optionListFree(ptr->option_lst);
prev = ptr;
ptr = ptr->list.next;
free(prev);
}
}

View File

@ -13,6 +13,7 @@ INTERNAL_SOURCES= \
Files.c \
Flags.c \
Input.c \
InputClass.c \
Layout.c \
Module.c \
Video.c \

View File

@ -48,6 +48,10 @@ 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);
/* Layout.c */
XF86ConfLayoutPtr xf86parseLayoutSection(void);
void xf86printLayoutSection(FILE *cf, XF86ConfLayoutPtr ptr);

View File

@ -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);

View File

@ -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);

View File

@ -338,6 +338,25 @@ typedef struct
}
xf86TriState;
typedef struct
{
GenericListRec list;
char *identifier;
char *driver;
char *match_product;
char *match_vendor;
char *match_device;
xf86TriState is_keyboard;
xf86TriState is_pointer;
xf86TriState is_joystick;
xf86TriState is_tablet;
xf86TriState is_touchpad;
xf86TriState is_touchscreen;
XF86OptionPtr option_lst;
char *comment;
}
XF86ConfInputClassRec, *XF86ConfInputClassPtr;
/* Values for adj_where */
#define CONF_ADJ_OBSOLETE -1
#define CONF_ADJ_ABSOLUTE 0
@ -446,6 +465,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;

View File

@ -273,7 +273,18 @@ typedef enum {
/* DRI Tokens */
GROUP,
BUFFERS
BUFFERS,
/* InputClass Tokens */
MATCH_PRODUCT,
MATCH_VENDOR,
MATCH_DEVICE_PATH,
MATCH_IS_KEYBOARD,
MATCH_IS_POINTER,
MATCH_IS_JOYSTICK,
MATCH_IS_TABLET,
MATCH_IS_TOUCHPAD,
MATCH_IS_TOUCHSCREEN
} ParserTokens;
#endif /* _xf86_tokens_h */

View File

@ -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