1676 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1676 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright 1995-1999 by Frederic Lepied, France. <Lepied@XFree86.org>
 | |
|  *
 | |
|  * Permission to use, copy, modify, distribute, and sell this software and its
 | |
|  * documentation for any purpose is  hereby granted without fee, provided that
 | |
|  * the  above copyright   notice appear  in   all  copies and  that both  that
 | |
|  * copyright  notice   and   this  permission   notice  appear  in  supporting
 | |
|  * documentation, and that   the  name of  Frederic   Lepied not  be  used  in
 | |
|  * advertising or publicity pertaining to distribution of the software without
 | |
|  * specific,  written      prior  permission.     Frederic  Lepied   makes  no
 | |
|  * representations about the suitability of this software for any purpose.  It
 | |
|  * is provided "as is" without express or implied warranty.
 | |
|  *
 | |
|  * FREDERIC  LEPIED DISCLAIMS ALL   WARRANTIES WITH REGARD  TO  THIS SOFTWARE,
 | |
|  * INCLUDING ALL IMPLIED   WARRANTIES OF MERCHANTABILITY  AND   FITNESS, IN NO
 | |
|  * EVENT  SHALL FREDERIC  LEPIED BE   LIABLE   FOR ANY  SPECIAL, INDIRECT   OR
 | |
|  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 | |
|  * DATA  OR PROFITS, WHETHER  IN  AN ACTION OF  CONTRACT,  NEGLIGENCE OR OTHER
 | |
|  * TORTIOUS  ACTION, ARISING    OUT OF OR   IN  CONNECTION  WITH THE USE    OR
 | |
|  * PERFORMANCE OF THIS SOFTWARE.
 | |
|  *
 | |
|  */
 | |
| /*
 | |
|  * Copyright (c) 2000-2002 by The XFree86 Project, Inc.
 | |
|  *
 | |
|  * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 | |
|  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 | |
|  * OTHER DEALINGS IN THE SOFTWARE.
 | |
|  *
 | |
|  * Except as contained in this notice, the name of the copyright holder(s)
 | |
|  * and author(s) shall not be used in advertising or otherwise to promote
 | |
|  * the sale, use or other dealings in this Software without prior written
 | |
|  * authorization from the copyright holder(s) and author(s).
 | |
|  */
 | |
| 
 | |
| #ifdef HAVE_XORG_CONFIG_H
 | |
| #include <xorg-config.h>
 | |
| #endif
 | |
| 
 | |
| #include <string.h>             /* InputClassMatches */
 | |
| #include <X11/Xfuncproto.h>
 | |
| #include <X11/Xmd.h>
 | |
| #include <X11/extensions/XI.h>
 | |
| #include <X11/extensions/XIproto.h>
 | |
| #include <X11/Xatom.h>
 | |
| 
 | |
| #include "dix/dix_priv.h"
 | |
| #include "dix/ptrveloc_priv.h"
 | |
| 
 | |
| #include "xf86.h"
 | |
| #include "xf86Priv.h"
 | |
| #include "xf86Config.h"
 | |
| #include "xf86Xinput.h"
 | |
| #include "xf86Optrec.h"
 | |
| #include "mipointer.h"
 | |
| #include "extinit.h"
 | |
| #include "loaderProcs.h"
 | |
| #include "systemd-logind.h"
 | |
| #include "exevents.h"           /* AddInputDevice */
 | |
| #include "exglobals.h"
 | |
| #include "eventstr.h"
 | |
| #include "inpututils.h"
 | |
| #include "optionstr.h"
 | |
| 
 | |
| #ifdef HAVE_FNMATCH_H
 | |
| #include <fnmatch.h>
 | |
| #endif
 | |
| #ifdef HAVE_SYS_UTSNAME_H
 | |
| #include <sys/utsname.h>
 | |
| #endif
 | |
| 
 | |
| #include <stdarg.h>
 | |
| #include <stdint.h>             /* for int64_t */
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <unistd.h>
 | |
| #ifdef HAVE_SYS_SYSMACROS_H
 | |
| #include <sys/sysmacros.h>
 | |
| #endif
 | |
| #ifdef HAVE_SYS_MKDEV_H
 | |
| #include <sys/mkdev.h>          /* for major() & minor() on Solaris */
 | |
| #endif
 | |
| 
 | |
| #include "mi.h"
 | |
| 
 | |
| #include <ptrveloc.h>           /* dix pointer acceleration */
 | |
| #include <xserver-properties.h>
 | |
| 
 | |
| #ifdef XFreeXDGA
 | |
| #include "dgaproc.h"
 | |
| #include "dgaproc_priv.h"
 | |
| #endif
 | |
| 
 | |
| #include "xkbsrv.h"
 | |
| 
 | |
| /* Valuator verification macro */
 | |
| #define XI_VERIFY_VALUATORS(num_valuators)					\
 | |
| 	if (num_valuators > MAX_VALUATORS) {					\
 | |
| 		xf86Msg(X_ERROR, "%s: num_valuator %d is greater than"		\
 | |
| 			" MAX_VALUATORS\n", __FUNCTION__, num_valuators);	\
 | |
| 		return;								\
 | |
| 	}
 | |
| 
 | |
| static int
 | |
|  xf86InputDevicePostInit(DeviceIntPtr dev);
 | |
| 
 | |
| typedef struct {
 | |
|     struct xorg_list node;
 | |
|     InputInfoPtr pInfo;
 | |
| } PausedInputDeviceRec;
 | |
| typedef PausedInputDeviceRec *PausedInputDevicePtr;
 | |
| 
 | |
| static struct xorg_list new_input_devices_list = {
 | |
|     .next = &new_input_devices_list,
 | |
|     .prev = &new_input_devices_list,
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Eval config and modify DeviceVelocityRec accordingly
 | |
|  */
 | |
| static void
 | |
| ProcessVelocityConfiguration(DeviceIntPtr pDev, const char *devname, void *list,
 | |
|                              DeviceVelocityPtr s)
 | |
| {
 | |
|     int tempi;
 | |
|     float tempf;
 | |
|     Atom float_prop = XIGetKnownProperty(XATOM_FLOAT);
 | |
|     Atom prop;
 | |
| 
 | |
|     if (!s)
 | |
|         return;
 | |
| 
 | |
|     /* common settings (available via device properties) */
 | |
|     tempf = xf86SetRealOption(list, "ConstantDeceleration", 1.0);
 | |
|     if (tempf != 1.0) {
 | |
|         xf86Msg(X_CONFIG, "%s: (accel) constant deceleration by %.1f\n",
 | |
|                 devname, tempf);
 | |
|         prop = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
 | |
|         XIChangeDeviceProperty(pDev, prop, float_prop, 32,
 | |
|                                PropModeReplace, 1, &tempf, FALSE);
 | |
|     }
 | |
| 
 | |
|     tempf = xf86SetRealOption(list, "AdaptiveDeceleration", 1.0);
 | |
|     if (tempf > 1.0) {
 | |
|         xf86Msg(X_CONFIG, "%s: (accel) adaptive deceleration by %.1f\n",
 | |
|                 devname, tempf);
 | |
|         prop = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION);
 | |
|         XIChangeDeviceProperty(pDev, prop, float_prop, 32,
 | |
|                                PropModeReplace, 1, &tempf, FALSE);
 | |
|     }
 | |
| 
 | |
|     /* select profile by number */
 | |
|     tempi = xf86SetIntOption(list, "AccelerationProfile",
 | |
|                              s->statistics.profile_number);
 | |
| 
 | |
|     prop = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
 | |
|     if (XIChangeDeviceProperty(pDev, prop, XA_INTEGER, 32,
 | |
|                                PropModeReplace, 1, &tempi, FALSE) == Success) {
 | |
|         xf86Msg(X_CONFIG, "%s: (accel) acceleration profile %i\n", devname,
 | |
|                 tempi);
 | |
|     }
 | |
|     else {
 | |
|         xf86Msg(X_CONFIG, "%s: (accel) acceleration profile %i is unknown\n",
 | |
|                 devname, tempi);
 | |
|     }
 | |
| 
 | |
|     /* set scaling */
 | |
|     tempf = xf86SetRealOption(list, "ExpectedRate", 0);
 | |
|     prop = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING);
 | |
|     if (tempf > 0) {
 | |
|         tempf = 1000.0 / tempf;
 | |
|         XIChangeDeviceProperty(pDev, prop, float_prop, 32,
 | |
|                                PropModeReplace, 1, &tempf, FALSE);
 | |
|     }
 | |
|     else {
 | |
|         tempf = xf86SetRealOption(list, "VelocityScale", s->corr_mul);
 | |
|         XIChangeDeviceProperty(pDev, prop, float_prop, 32,
 | |
|                                PropModeReplace, 1, &tempf, FALSE);
 | |
|     }
 | |
| 
 | |
|     tempi = xf86SetIntOption(list, "VelocityTrackerCount", -1);
 | |
|     if (tempi > 1)
 | |
|         InitTrackers(s, tempi);
 | |
| 
 | |
|     s->initial_range = xf86SetIntOption(list, "VelocityInitialRange",
 | |
|                                         s->initial_range);
 | |
| 
 | |
|     s->max_diff = xf86SetRealOption(list, "VelocityAbsDiff", s->max_diff);
 | |
| 
 | |
|     tempf = xf86SetRealOption(list, "VelocityRelDiff", -1);
 | |
|     if (tempf >= 0) {
 | |
|         xf86Msg(X_CONFIG, "%s: (accel) max rel. velocity difference: %.1f%%\n",
 | |
|                 devname, tempf * 100.0);
 | |
|         s->max_rel_diff = tempf;
 | |
|     }
 | |
| 
 | |
|     /*  Configure softening. If const deceleration is used, this is expected
 | |
|      *  to provide better subpixel information so we enable
 | |
|      *  softening by default only if ConstantDeceleration is not used
 | |
|      */
 | |
|     s->use_softening = xf86SetBoolOption(list, "Softening",
 | |
|                                          s->const_acceleration == 1.0);
 | |
| 
 | |
|     s->average_accel = xf86SetBoolOption(list, "AccelerationProfileAveraging",
 | |
|                                          s->average_accel);
 | |
| 
 | |
|     s->reset_time = xf86SetIntOption(list, "VelocityReset", s->reset_time);
 | |
| }
 | |
| 
 | |
| static void
 | |
| ApplyAccelerationSettings(DeviceIntPtr dev)
 | |
| {
 | |
|     int scheme, i;
 | |
|     DeviceVelocityPtr pVel;
 | |
|     InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
 | |
|     char *schemeStr;
 | |
| 
 | |
|     if (dev->valuator && dev->ptrfeed) {
 | |
|         schemeStr = xf86SetStrOption(pInfo->options, "AccelerationScheme", "");
 | |
| 
 | |
|         scheme = dev->valuator->accelScheme.number;
 | |
| 
 | |
|         if (!xf86NameCmp(schemeStr, "predictable"))
 | |
|             scheme = PtrAccelPredictable;
 | |
| 
 | |
|         if (!xf86NameCmp(schemeStr, "lightweight"))
 | |
|             scheme = PtrAccelLightweight;
 | |
| 
 | |
|         if (!xf86NameCmp(schemeStr, "none"))
 | |
|             scheme = PtrAccelNoOp;
 | |
| 
 | |
|         /* reinit scheme if needed */
 | |
|         if (dev->valuator->accelScheme.number != scheme) {
 | |
|             if (dev->valuator->accelScheme.AccelCleanupProc) {
 | |
|                 dev->valuator->accelScheme.AccelCleanupProc(dev);
 | |
|             }
 | |
| 
 | |
|             if (InitPointerAccelerationScheme(dev, scheme)) {
 | |
|                 xf86Msg(X_CONFIG, "%s: (accel) selected scheme %s/%i\n",
 | |
|                         pInfo->name, schemeStr, scheme);
 | |
|             }
 | |
|             else {
 | |
|                 xf86Msg(X_CONFIG, "%s: (accel) could not init scheme %s\n",
 | |
|                         pInfo->name, schemeStr);
 | |
|                 scheme = dev->valuator->accelScheme.number;
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             xf86Msg(X_CONFIG, "%s: (accel) keeping acceleration scheme %i\n",
 | |
|                     pInfo->name, scheme);
 | |
|         }
 | |
| 
 | |
|         free(schemeStr);
 | |
| 
 | |
|         /* process special configuration */
 | |
|         switch (scheme) {
 | |
|         case PtrAccelPredictable:
 | |
|             pVel = GetDevicePredictableAccelData(dev);
 | |
|             ProcessVelocityConfiguration(dev, pInfo->name, pInfo->options,
 | |
|                                          pVel);
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         i = xf86SetIntOption(pInfo->options, "AccelerationNumerator",
 | |
|                              dev->ptrfeed->ctrl.num);
 | |
|         if (i >= 0)
 | |
|             dev->ptrfeed->ctrl.num = i;
 | |
| 
 | |
|         i = xf86SetIntOption(pInfo->options, "AccelerationDenominator",
 | |
|                              dev->ptrfeed->ctrl.den);
 | |
|         if (i > 0)
 | |
|             dev->ptrfeed->ctrl.den = i;
 | |
| 
 | |
|         i = xf86SetIntOption(pInfo->options, "AccelerationThreshold",
 | |
|                              dev->ptrfeed->ctrl.threshold);
 | |
|         if (i >= 0)
 | |
|             dev->ptrfeed->ctrl.threshold = i;
 | |
| 
 | |
|         xf86Msg(X_CONFIG, "%s: (accel) acceleration factor: %.3f\n",
 | |
|                 pInfo->name, ((float) dev->ptrfeed->ctrl.num) /
 | |
|                 ((float) dev->ptrfeed->ctrl.den));
 | |
|         xf86Msg(X_CONFIG, "%s: (accel) acceleration threshold: %i\n",
 | |
|                 pInfo->name, dev->ptrfeed->ctrl.threshold);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| ApplyTransformationMatrix(DeviceIntPtr dev)
 | |
| {
 | |
|     InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
 | |
|     char *str;
 | |
|     int rc;
 | |
|     float matrix[9] = { 0 };
 | |
| 
 | |
|     if (!dev->valuator)
 | |
|         return;
 | |
| 
 | |
|     str = xf86SetStrOption(pInfo->options, "TransformationMatrix", NULL);
 | |
|     if (!str)
 | |
|         return;
 | |
| 
 | |
|     rc = sscanf(str, "%f %f %f %f %f %f %f %f %f", &matrix[0], &matrix[1],
 | |
|                 &matrix[2], &matrix[3], &matrix[4], &matrix[5], &matrix[6],
 | |
|                 &matrix[7], &matrix[8]);
 | |
|     if (rc != 9) {
 | |
|         xf86Msg(X_ERROR,
 | |
|                 "%s: invalid format for transformation matrix. Ignoring configuration.\n",
 | |
|                 pInfo->name);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     XIChangeDeviceProperty(dev, XIGetKnownProperty(XI_PROP_TRANSFORM),
 | |
|                            XIGetKnownProperty(XATOM_FLOAT), 32,
 | |
|                            PropModeReplace, 9, matrix, FALSE);
 | |
| }
 | |
| 
 | |
| static void
 | |
| ApplyAutoRepeat(DeviceIntPtr dev)
 | |
| {
 | |
|     InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
 | |
|     XkbSrvInfoPtr xkbi;
 | |
|     char *repeatStr;
 | |
|     long delay, rate;
 | |
| 
 | |
|     if (!dev->key)
 | |
|         return;
 | |
| 
 | |
|     xkbi = dev->key->xkbInfo;
 | |
| 
 | |
|     repeatStr = xf86SetStrOption(pInfo->options, "AutoRepeat", NULL);
 | |
|     if (!repeatStr)
 | |
|         return;
 | |
| 
 | |
|     if (sscanf(repeatStr, "%ld %ld", &delay, &rate) != 2) {
 | |
|         xf86Msg(X_ERROR, "\"%s\" is not a valid AutoRepeat value\n", repeatStr);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     xf86Msg(X_CONFIG, "AutoRepeat: %ld %ld\n", delay, rate);
 | |
|     xkbi->desc->ctrls->repeat_delay = delay;
 | |
|     xkbi->desc->ctrls->repeat_interval = 1000 / rate;
 | |
| }
 | |
| 
 | |
| /***********************************************************************
 | |
|  *
 | |
|  * xf86ProcessCommonOptions --
 | |
|  *
 | |
|  *	Process global options.
 | |
|  *
 | |
|  ***********************************************************************
 | |
|  */
 | |
| void
 | |
| xf86ProcessCommonOptions(InputInfoPtr pInfo, XF86OptionPtr list)
 | |
| {
 | |
|     if (xf86SetBoolOption(list, "Floating", 0) ||
 | |
|         !xf86SetBoolOption(list, "AlwaysCore", 1) ||
 | |
|         !xf86SetBoolOption(list, "SendCoreEvents", 1) ||
 | |
|         !xf86SetBoolOption(list, "CorePointer", 1) ||
 | |
|         !xf86SetBoolOption(list, "CoreKeyboard", 1)) {
 | |
|         xf86Msg(X_CONFIG, "%s: doesn't report core events\n", pInfo->name);
 | |
|     }
 | |
|     else {
 | |
|         pInfo->flags |= XI86_ALWAYS_CORE;
 | |
|         xf86Msg(X_CONFIG, "%s: always reports core events\n", pInfo->name);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /***********************************************************************
 | |
|  *
 | |
|  * xf86ActivateDevice --
 | |
|  *
 | |
|  *	Initialize an input device.
 | |
|  *
 | |
|  * Returns TRUE on success, or FALSE otherwise.
 | |
|  ***********************************************************************
 | |
|  */
 | |
| static DeviceIntPtr
 | |
| xf86ActivateDevice(InputInfoPtr pInfo)
 | |
| {
 | |
|     DeviceIntPtr dev;
 | |
|     Atom atom;
 | |
| 
 | |
|     dev = AddInputDevice(serverClient, pInfo->device_control, TRUE);
 | |
| 
 | |
|     if (dev == NULL) {
 | |
|         xf86Msg(X_ERROR, "Too many input devices. Ignoring %s\n", pInfo->name);
 | |
|         pInfo->dev = NULL;
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     atom = MakeAtom(pInfo->type_name, strlen(pInfo->type_name), TRUE);
 | |
|     AssignTypeAndName(dev, atom, pInfo->name);
 | |
|     dev->public.devicePrivate = pInfo;
 | |
|     pInfo->dev = dev;
 | |
| 
 | |
|     dev->coreEvents = pInfo->flags & XI86_ALWAYS_CORE;
 | |
|     dev->type = SLAVE;
 | |
|     dev->spriteInfo->spriteOwner = FALSE;
 | |
| 
 | |
|     dev->config_info = xf86SetStrOption(pInfo->options, "config_info", NULL);
 | |
| 
 | |
|     if (serverGeneration == 1)
 | |
|         xf86Msg(X_INFO,
 | |
|                 "XINPUT: Adding extended input device \"%s\" (type: %s, id %d)\n",
 | |
|                 pInfo->name, pInfo->type_name, dev->id);
 | |
| 
 | |
|     return dev;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  *
 | |
|  * Caller:	ProcXSetDeviceMode
 | |
|  *
 | |
|  * Change the mode of an extension device.
 | |
|  * This function is used to change the mode of a device from reporting
 | |
|  * relative motion to reporting absolute positional information, and
 | |
|  * vice versa.
 | |
|  * The default implementation below is that no such devices are supported.
 | |
|  *
 | |
|  ***********************************************************************
 | |
|  */
 | |
| 
 | |
| int
 | |
| SetDeviceMode(ClientPtr client, DeviceIntPtr dev, int mode)
 | |
| {
 | |
|     InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
 | |
| 
 | |
|     if (pInfo->switch_mode) {
 | |
|         return (*pInfo->switch_mode) (client, dev, mode);
 | |
|     }
 | |
|     else
 | |
|         return BadMatch;
 | |
| }
 | |
| 
 | |
| /***********************************************************************
 | |
|  *
 | |
|  * Caller:	ProcXSetDeviceValuators
 | |
|  *
 | |
|  * Set the value of valuators on an extension input device.
 | |
|  * This function is used to set the initial value of valuators on
 | |
|  * those input devices that are capable of reporting either relative
 | |
|  * motion or an absolute position, and allow an initial position to be set.
 | |
|  * The default implementation below is that no such devices are supported.
 | |
|  *
 | |
|  ***********************************************************************
 | |
|  */
 | |
| 
 | |
| int
 | |
| SetDeviceValuators(ClientPtr client, DeviceIntPtr dev, int *valuators,
 | |
|                    int first_valuator, int num_valuators)
 | |
| {
 | |
|     InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
 | |
| 
 | |
|     if (pInfo->set_device_valuators)
 | |
|         return (*pInfo->set_device_valuators) (pInfo, valuators, first_valuator,
 | |
|                                                num_valuators);
 | |
| 
 | |
|     return BadMatch;
 | |
| }
 | |
| 
 | |
| /***********************************************************************
 | |
|  *
 | |
|  * Caller:	ProcXChangeDeviceControl
 | |
|  *
 | |
|  * Change the specified device controls on an extension input device.
 | |
|  *
 | |
|  ***********************************************************************
 | |
|  */
 | |
| 
 | |
| int
 | |
| ChangeDeviceControl(ClientPtr client, DeviceIntPtr dev, xDeviceCtl * control)
 | |
| {
 | |
|     InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
 | |
| 
 | |
|     if (!pInfo->control_proc) {
 | |
|         switch (control->control) {
 | |
|         case DEVICE_CORE:
 | |
|         case DEVICE_ABS_CALIB:
 | |
|         case DEVICE_ABS_AREA:
 | |
|             return BadMatch;
 | |
|         case DEVICE_RESOLUTION:
 | |
|         case DEVICE_ENABLE:
 | |
|             return Success;
 | |
|         default:
 | |
|             return BadMatch;
 | |
|         }
 | |
|     }
 | |
|     else {
 | |
|         return (*pInfo->control_proc) (pInfo, control);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Get the operating system name from uname and store it statically to avoid
 | |
|  * repeating the system call each time MatchOS is checked.
 | |
|  */
 | |
| static const char *
 | |
| HostOS(void)
 | |
| {
 | |
| #ifdef HAVE_SYS_UTSNAME_H
 | |
|     struct utsname name;
 | |
|     static char host_os[sizeof(name.sysname)] = "";
 | |
| 
 | |
|     if (*host_os == '\0') {
 | |
|         if (uname(&name) >= 0)
 | |
|             strlcpy(host_os, name.sysname, sizeof(host_os));
 | |
|         else {
 | |
|             strlcpy(host_os, "unknown", sizeof(host_os));
 | |
|         }
 | |
|     }
 | |
|     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
 | |
| static int
 | |
| match_path_pattern(const char *attr, const char *pattern)
 | |
| {
 | |
|     return fnmatch(pattern, attr, FNM_PATHNAME);
 | |
| }
 | |
| #else
 | |
| #define match_path_pattern match_substring
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * If no Layout section is found, xf86ServerLayout.id becomes "(implicit)"
 | |
|  * It is convenient that "" in patterns means "no explicit layout"
 | |
|  */
 | |
| static int
 | |
| match_string_implicit(const char *attr, const char *pattern)
 | |
| {
 | |
|     if (strlen(pattern)) {
 | |
|         return strcmp(attr, pattern);
 | |
|     }
 | |
|     else {
 | |
|         return strcmp(attr, "(implicit)");
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 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 xorg_list *patterns,
 | |
|                int (*compare) (const char *attr, const char *pattern))
 | |
| {
 | |
|     const xf86MatchGroup *group;
 | |
| 
 | |
|     /* If there are no patterns, accept the match */
 | |
|     if (xorg_list_is_empty(patterns))
 | |
|         return TRUE;
 | |
| 
 | |
|     /*
 | |
|      * Iterate the list of patterns ensuring each entry has a
 | |
|      * match. Each list entry is a separate Match line of the same type.
 | |
|      */
 | |
|     xorg_list_for_each_entry(group, patterns, entry) {
 | |
|         char *const *cur;
 | |
|         Bool is_negated = group->is_negated;
 | |
|         Bool match = is_negated;
 | |
| 
 | |
|         /* If there's a pattern but no attribute, we reject the match for a
 | |
|          * MatchFoo directive, and accept it for a NoMatchFoo directive
 | |
|          */
 | |
|         if (!attr)
 | |
|             return is_negated;
 | |
| 
 | |
|         for (cur = group->values; *cur; cur++)
 | |
|             if ((*compare) (attr, *cur) == 0) {
 | |
|                 match = !is_negated;
 | |
|                 break;
 | |
|             }
 | |
|         if (!match)
 | |
|             return FALSE;
 | |
|     }
 | |
| 
 | |
|     /* 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 InputInfoPtr 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 (!xorg_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)) {
 | |
|                 match = TRUE;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         if (!match)
 | |
|             return FALSE;
 | |
|     }
 | |
| 
 | |
|     /* MatchLayout string */
 | |
|     if (!xorg_list_is_empty(&iclass->match_layout)) {
 | |
|         if (!MatchAttrToken(xf86ConfigLayout.id,
 | |
|                             &iclass->match_layout, match_string_implicit))
 | |
|             return FALSE;
 | |
|     }
 | |
| 
 | |
|     /* MatchIs* booleans */
 | |
|     if (iclass->is_keyboard.set &&
 | |
|         iclass->is_keyboard.val != ! !(attrs->flags & (ATTR_KEY|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_tablet_pad.set &&
 | |
|         iclass->is_tablet_pad.val != ! !(attrs->flags & ATTR_TABLET_PAD))
 | |
|         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. Options in each InputClass
 | |
|  * section have more priority than the original device configuration as
 | |
|  * well as any previous InputClass sections.
 | |
|  */
 | |
| static int
 | |
| MergeInputClasses(const InputInfoPtr idev, const InputAttributes * attrs)
 | |
| {
 | |
|     XF86ConfInputClassPtr cl;
 | |
|     XF86OptionPtr classopts;
 | |
| 
 | |
|     for (cl = xf86configptr->conf_inputclass_lst; cl; cl = cl->list.next) {
 | |
|         if (!InputClassMatches(cl, idev, attrs))
 | |
|             continue;
 | |
| 
 | |
|         /* Collect class options and driver settings */
 | |
|         classopts = xf86optionListDup(cl->option_lst);
 | |
|         if (cl->driver) {
 | |
|             free((void *) 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->name, cl->identifier);
 | |
|         idev->options = xf86optionListMerge(idev->options, classopts);
 | |
|     }
 | |
| 
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Iterate the list of classes and look for Option "Ignore". Return the
 | |
|  * value of the last matching class and holler when returning TRUE.
 | |
|  */
 | |
| static Bool
 | |
| IgnoreInputClass(const InputInfoPtr 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, idev, attrs))
 | |
|             continue;
 | |
|         if (xf86findOption(cl->option_lst, "Ignore")) {
 | |
|             ignore = xf86CheckBoolOption(cl->option_lst, "Ignore", FALSE);
 | |
|             ignore_class = cl->identifier;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (ignore)
 | |
|         xf86Msg(X_CONFIG, "%s: Ignoring device from InputClass \"%s\"\n",
 | |
|                 idev->name, ignore_class);
 | |
|     return ignore;
 | |
| }
 | |
| 
 | |
| InputInfoPtr
 | |
| xf86AllocateInput(void)
 | |
| {
 | |
|     InputInfoPtr pInfo;
 | |
| 
 | |
|     pInfo = calloc(sizeof(*pInfo), 1);
 | |
|     if (!pInfo)
 | |
|         return NULL;
 | |
| 
 | |
|     pInfo->fd = -1;
 | |
|     pInfo->type_name = "UNKNOWN";
 | |
| 
 | |
|     return pInfo;
 | |
| }
 | |
| 
 | |
| /* Append InputInfoRec to the tail of xf86InputDevs. */
 | |
| static void
 | |
| xf86AddInput(InputDriverPtr drv, InputInfoPtr pInfo)
 | |
| {
 | |
|     InputInfoPtr *prev = NULL;
 | |
| 
 | |
|     pInfo->drv = drv;
 | |
|     pInfo->module = DuplicateModule(drv->module, NULL);
 | |
| 
 | |
|     for (prev = &xf86InputDevs; *prev; prev = &(*prev)->next);
 | |
| 
 | |
|     *prev = pInfo;
 | |
|     pInfo->next = NULL;
 | |
| 
 | |
|     xf86CollectInputOptions(pInfo, (const char **) drv->default_options);
 | |
|     xf86OptionListReport(pInfo->options);
 | |
|     xf86ProcessCommonOptions(pInfo, pInfo->options);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Remove an entry from xf86InputDevs and free all the device's information.
 | |
|  */
 | |
| void
 | |
| xf86DeleteInput(InputInfoPtr pInp, int flags)
 | |
| {
 | |
|     /* First check if the inputdev is valid. */
 | |
|     if (pInp == NULL)
 | |
|         return;
 | |
| 
 | |
|     if (pInp->module)
 | |
|         UnloadModule(pInp->module);
 | |
| 
 | |
|     /* This should *really* be handled in drv->UnInit(dev) call instead, but
 | |
|      * if the driver forgets about it make sure we free it or at least crash
 | |
|      * with flying colors */
 | |
|     free(pInp->private);
 | |
| 
 | |
|     FreeInputAttributes(pInp->attrs);
 | |
| 
 | |
|     if (pInp->flags & XI86_SERVER_FD)
 | |
|         systemd_logind_release_fd(pInp->major, pInp->minor, pInp->fd);
 | |
| 
 | |
|     /* Remove the entry from the list. */
 | |
|     if (pInp == xf86InputDevs)
 | |
|         xf86InputDevs = pInp->next;
 | |
|     else {
 | |
|         InputInfoPtr p = xf86InputDevs;
 | |
| 
 | |
|         while (p && p->next != pInp)
 | |
|             p = p->next;
 | |
|         if (p)
 | |
|             p->next = pInp->next;
 | |
|         /* Else the entry wasn't in the xf86InputDevs list (ignore this). */
 | |
|     }
 | |
| 
 | |
|     free((void *) pInp->driver);
 | |
|     free((void *) pInp->name);
 | |
|     xf86optionListFree(pInp->options);
 | |
|     free(pInp);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Apply backend-specific initialization. Invoked after ActivateDevice(),
 | |
|  * i.e. after the driver successfully completed DEVICE_INIT and the device
 | |
|  * is advertised.
 | |
|  * @param dev the device
 | |
|  * @return Success or an error code
 | |
|  */
 | |
| static int
 | |
| xf86InputDevicePostInit(DeviceIntPtr dev)
 | |
| {
 | |
|     ApplyAccelerationSettings(dev);
 | |
|     ApplyTransformationMatrix(dev);
 | |
|     ApplyAutoRepeat(dev);
 | |
|     return Success;
 | |
| }
 | |
| 
 | |
| static void
 | |
| xf86stat(const char *path, int *maj, int *min)
 | |
| {
 | |
|     struct stat st;
 | |
| 
 | |
|     if (stat(path, &st) == -1)
 | |
|         return;
 | |
| 
 | |
|     *maj = major(st.st_rdev);
 | |
|     *min = minor(st.st_rdev);
 | |
| }
 | |
| 
 | |
| static inline InputDriverPtr
 | |
| xf86LoadInputDriver(const char *driver_name)
 | |
| {
 | |
|     InputDriverPtr drv = NULL;
 | |
| 
 | |
|     /* Memory leak for every attached device if we don't
 | |
|      * test if the module is already loaded first */
 | |
|     drv = xf86LookupInputDriver(driver_name);
 | |
|     if (!drv) {
 | |
|         if (xf86LoadOneModule(driver_name, NULL))
 | |
|             drv = xf86LookupInputDriver(driver_name);
 | |
|     }
 | |
| 
 | |
|     return drv;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Create a new input device, activate and enable it.
 | |
|  *
 | |
|  * Possible return codes:
 | |
|  *    BadName .. a bad driver name was supplied.
 | |
|  *    BadImplementation ... The driver does not have a PreInit function. This
 | |
|  *                          is a driver bug.
 | |
|  *    BadMatch .. device initialization failed.
 | |
|  *    BadAlloc .. too many input devices
 | |
|  *
 | |
|  * @param idev The device, already set up with identifier, driver, and the
 | |
|  * options.
 | |
|  * @param pdev Pointer to the new device, if Success was reported.
 | |
|  * @param enable Enable the device after activating it.
 | |
|  *
 | |
|  * @return Success or an error code
 | |
|  */
 | |
| _X_INTERNAL int
 | |
| xf86NewInputDevice(InputInfoPtr pInfo, DeviceIntPtr *pdev, BOOL enable)
 | |
| {
 | |
|     InputDriverPtr drv = NULL;
 | |
|     DeviceIntPtr dev = NULL;
 | |
|     Bool paused = FALSE;
 | |
|     int rval;
 | |
|     char *path = NULL;
 | |
| 
 | |
|     drv = xf86LoadInputDriver(pInfo->driver);
 | |
|     if (!drv) {
 | |
|         xf86Msg(X_ERROR, "No input driver matching `%s'\n", pInfo->driver);
 | |
| 
 | |
|         if (strlen(FALLBACK_INPUT_DRIVER) > 0) {
 | |
|             xf86Msg(X_INFO, "Falling back to input driver `%s'\n",
 | |
|                     FALLBACK_INPUT_DRIVER);
 | |
|             drv = xf86LoadInputDriver(FALLBACK_INPUT_DRIVER);
 | |
|             if (drv) {
 | |
|                 free(pInfo->driver);
 | |
|                 pInfo->driver = strdup(FALLBACK_INPUT_DRIVER);
 | |
|             }
 | |
|         }
 | |
|         if (!drv) {
 | |
|             rval = BadName;
 | |
|             goto unwind;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     xf86Msg(X_INFO, "Using input driver '%s' for '%s'\n", drv->driverName,
 | |
|             pInfo->name);
 | |
| 
 | |
|     if (!drv->PreInit) {
 | |
|         xf86Msg(X_ERROR,
 | |
|                 "Input driver `%s' has no PreInit function (ignoring)\n",
 | |
|                 drv->driverName);
 | |
|         rval = BadImplementation;
 | |
|         goto unwind;
 | |
|     }
 | |
| 
 | |
|     path = xf86CheckStrOption(pInfo->options, "Device", NULL);
 | |
|     if (path && pInfo->major == 0 && pInfo->minor == 0)
 | |
|         xf86stat(path, &pInfo->major, &pInfo->minor);
 | |
| 
 | |
|     if (path && (drv->capabilities & XI86_DRV_CAP_SERVER_FD)){
 | |
|         int fd = systemd_logind_take_fd(pInfo->major, pInfo->minor,
 | |
|                                         path, &paused);
 | |
|         if (fd != -1) {
 | |
|             if (paused) {
 | |
|                 /* Put on new_input_devices list for delayed probe */
 | |
|                 PausedInputDevicePtr new_device = xnfalloc(sizeof *new_device);
 | |
|                 new_device->pInfo = pInfo;
 | |
| 
 | |
|                 xorg_list_append(&new_device->node, &new_input_devices_list);
 | |
|                 systemd_logind_release_fd(pInfo->major, pInfo->minor, fd);
 | |
|                 free(path);
 | |
|                 return BadMatch;
 | |
|             }
 | |
|             pInfo->fd = fd;
 | |
|             pInfo->flags |= XI86_SERVER_FD;
 | |
|             pInfo->options = xf86ReplaceIntOption(pInfo->options, "fd", fd);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     free(path);
 | |
| 
 | |
|     xf86AddInput(drv, pInfo);
 | |
| 
 | |
|     input_lock();
 | |
|     rval = drv->PreInit(drv, pInfo, 0);
 | |
|     input_unlock();
 | |
| 
 | |
|     if (rval != Success) {
 | |
|         xf86Msg(X_ERROR, "PreInit returned %d for \"%s\"\n", rval, pInfo->name);
 | |
|         goto unwind;
 | |
|     }
 | |
| 
 | |
|     if (!(dev = xf86ActivateDevice(pInfo))) {
 | |
|         rval = BadAlloc;
 | |
|         goto unwind;
 | |
|     }
 | |
| 
 | |
|     rval = ActivateDevice(dev, TRUE);
 | |
|     if (rval != Success) {
 | |
|         xf86Msg(X_ERROR, "Couldn't init device \"%s\"\n", pInfo->name);
 | |
|         RemoveDevice(dev, TRUE);
 | |
|         goto unwind;
 | |
|     }
 | |
| 
 | |
|     rval = xf86InputDevicePostInit(dev);
 | |
|     if (rval != Success) {
 | |
|         xf86Msg(X_ERROR, "Couldn't post-init device \"%s\"\n", pInfo->name);
 | |
|         RemoveDevice(dev, TRUE);
 | |
|         goto unwind;
 | |
|     }
 | |
| 
 | |
|     /* Enable it if it's properly initialised and we're currently in the VT */
 | |
|     if (enable && dev->inited && dev->startup && xf86VTOwner()) {
 | |
|         input_lock();
 | |
|         EnableDevice(dev, TRUE);
 | |
|         if (!dev->enabled) {
 | |
|             xf86Msg(X_ERROR, "Couldn't init device \"%s\"\n", pInfo->name);
 | |
|             RemoveDevice(dev, TRUE);
 | |
|             rval = BadMatch;
 | |
|             input_unlock();
 | |
|             goto unwind;
 | |
|         }
 | |
|         /* send enter/leave event, update sprite window */
 | |
|         CheckMotion(NULL, dev);
 | |
|         input_unlock();
 | |
|     }
 | |
| 
 | |
|     *pdev = dev;
 | |
|     return Success;
 | |
| 
 | |
|  unwind:
 | |
|     if (pInfo) {
 | |
|         if (drv && drv->UnInit)
 | |
|             drv->UnInit(drv, pInfo, 0);
 | |
|         else
 | |
|             xf86DeleteInput(pInfo, 0);
 | |
|     }
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| int
 | |
| NewInputDeviceRequest(InputOption *options, InputAttributes * attrs,
 | |
|                       DeviceIntPtr *pdev)
 | |
| {
 | |
|     InputInfoPtr pInfo = NULL;
 | |
|     InputOption *option = NULL;
 | |
|     int rval = Success;
 | |
|     int is_auto = 0;
 | |
| 
 | |
|     pInfo = xf86AllocateInput();
 | |
|     if (!pInfo)
 | |
|         return BadAlloc;
 | |
| 
 | |
|     nt_list_for_each_entry(option, options, list.next) {
 | |
|         const char *key = input_option_get_key(option);
 | |
|         const char *value = input_option_get_value(option);
 | |
| 
 | |
|         if (strcasecmp(key, "driver") == 0) {
 | |
|             if (pInfo->driver) {
 | |
|                 rval = BadRequest;
 | |
|                 goto unwind;
 | |
|             }
 | |
|             pInfo->driver = xstrdup(value);
 | |
|             if (!pInfo->driver) {
 | |
|                 rval = BadAlloc;
 | |
|                 goto unwind;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (strcasecmp(key, "name") == 0 || strcasecmp(key, "identifier") == 0) {
 | |
|             if (pInfo->name) {
 | |
|                 rval = BadRequest;
 | |
|                 goto unwind;
 | |
|             }
 | |
|             pInfo->name = xstrdup(value);
 | |
|             if (!pInfo->name) {
 | |
|                 rval = BadAlloc;
 | |
|                 goto unwind;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (strcmp(key, "_source") == 0 &&
 | |
|             (strcmp(value, "server/hal") == 0 ||
 | |
|              strcmp(value, "server/udev") == 0 ||
 | |
|              strcmp(value, "server/wscons") == 0)) {
 | |
|             is_auto = 1;
 | |
|             if (!xf86Info.autoAddDevices) {
 | |
|                 rval = BadMatch;
 | |
|                 goto unwind;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (strcmp(key, "major") == 0)
 | |
|             pInfo->major = atoi(value);
 | |
| 
 | |
|         if (strcmp(key, "minor") == 0)
 | |
|             pInfo->minor = atoi(value);
 | |
|     }
 | |
| 
 | |
|     nt_list_for_each_entry(option, options, list.next) {
 | |
|         /* Copy option key/value strings from the provided list */
 | |
|         pInfo->options = xf86AddNewOption(pInfo->options,
 | |
|                                           input_option_get_key(option),
 | |
|                                           input_option_get_value(option));
 | |
|     }
 | |
| 
 | |
|     /* Apply InputClass settings */
 | |
|     if (attrs) {
 | |
|         if (IgnoreInputClass(pInfo, attrs)) {
 | |
|             rval = BadIDChoice;
 | |
|             goto unwind;
 | |
|         }
 | |
| 
 | |
|         rval = MergeInputClasses(pInfo, attrs);
 | |
|         if (rval != Success)
 | |
|             goto unwind;
 | |
| 
 | |
|         pInfo->attrs = DuplicateInputAttributes(attrs);
 | |
|     }
 | |
| 
 | |
|     if (!pInfo->name) {
 | |
|         xf86Msg(X_INFO, "No identifier specified, ignoring this device.\n");
 | |
|         rval = BadRequest;
 | |
|         goto unwind;
 | |
|     }
 | |
| 
 | |
|     if (!pInfo->driver) {
 | |
|         xf86Msg(X_INFO, "No input driver specified, ignoring this device.\n");
 | |
|         xf86Msg(X_INFO,
 | |
|                 "This device may have been added with another device file.\n");
 | |
|         rval = BadRequest;
 | |
|         goto unwind;
 | |
|     }
 | |
| 
 | |
|     rval = xf86NewInputDevice(pInfo, pdev,
 | |
|                               (!is_auto ||
 | |
|                                (is_auto && xf86Info.autoEnableDevices)));
 | |
| 
 | |
|     return rval;
 | |
| 
 | |
|  unwind:
 | |
|     if (is_auto && !xf86Info.autoAddDevices)
 | |
|         xf86Msg(X_INFO, "AutoAddDevices is off - not adding device.\n");
 | |
|     xf86DeleteInput(pInfo, 0);
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| void
 | |
| DeleteInputDeviceRequest(DeviceIntPtr pDev)
 | |
| {
 | |
|     InputInfoPtr pInfo = (InputInfoPtr) pDev->public.devicePrivate;
 | |
|     InputDriverPtr drv = NULL;
 | |
|     Bool isMaster = IsMaster(pDev);
 | |
| 
 | |
|     if (pInfo)                  /* need to get these before RemoveDevice */
 | |
|         drv = pInfo->drv;
 | |
| 
 | |
|     input_lock();
 | |
|     RemoveDevice(pDev, TRUE);
 | |
| 
 | |
|     if (!isMaster && pInfo != NULL) {
 | |
|         if (drv->UnInit)
 | |
|             drv->UnInit(drv, pInfo, 0);
 | |
|         else
 | |
|             xf86DeleteInput(pInfo, 0);
 | |
|     }
 | |
|     input_unlock();
 | |
| }
 | |
| 
 | |
| void
 | |
| RemoveInputDeviceTraces(const char *config_info)
 | |
| {
 | |
|     PausedInputDevicePtr d, tmp;
 | |
| 
 | |
|     xorg_list_for_each_entry_safe(d, tmp, &new_input_devices_list, node) {
 | |
|         const char *ci = xf86findOptionValue(d->pInfo->options, "config_info");
 | |
|         if (!ci || strcmp(ci, config_info) != 0)
 | |
|             continue;
 | |
| 
 | |
|         xorg_list_del(&d->node);
 | |
|         free(d);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * convenient functions to post events
 | |
|  */
 | |
| 
 | |
| void
 | |
| xf86PostMotionEvent(DeviceIntPtr device,
 | |
|                     int is_absolute, int first_valuator, int num_valuators, ...)
 | |
| {
 | |
|     va_list var;
 | |
|     int i = 0;
 | |
|     ValuatorMask mask;
 | |
| 
 | |
|     XI_VERIFY_VALUATORS(num_valuators);
 | |
| 
 | |
|     valuator_mask_zero(&mask);
 | |
|     va_start(var, num_valuators);
 | |
|     for (i = 0; i < num_valuators; i++)
 | |
|         valuator_mask_set(&mask, first_valuator + i, va_arg(var, int));
 | |
| 
 | |
|     va_end(var);
 | |
| 
 | |
|     xf86PostMotionEventM(device, is_absolute, &mask);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostMotionEventP(DeviceIntPtr device,
 | |
|                      int is_absolute,
 | |
|                      int first_valuator,
 | |
|                      int num_valuators, const int *valuators)
 | |
| {
 | |
|     ValuatorMask mask;
 | |
| 
 | |
|     XI_VERIFY_VALUATORS(num_valuators);
 | |
| 
 | |
|     valuator_mask_set_range(&mask, first_valuator, num_valuators, valuators);
 | |
|     xf86PostMotionEventM(device, is_absolute, &mask);
 | |
| }
 | |
| 
 | |
| static int
 | |
| xf86CheckMotionEvent4DGA(DeviceIntPtr device, int is_absolute,
 | |
|                          const ValuatorMask *mask)
 | |
| {
 | |
|     int stolen = 0;
 | |
| 
 | |
| #ifdef XFreeXDGA
 | |
|     ScreenPtr scr = NULL;
 | |
|     int idx = 0, i;
 | |
| 
 | |
|     /* The evdev driver may not always send all axes across. */
 | |
|     if (valuator_mask_isset(mask, 0) || valuator_mask_isset(mask, 1)) {
 | |
|         scr = miPointerGetScreen(device);
 | |
|         if (scr) {
 | |
|             int dx = 0, dy = 0;
 | |
| 
 | |
|             idx = scr->myNum;
 | |
| 
 | |
|             if (valuator_mask_isset(mask, 0)) {
 | |
|                 dx = valuator_mask_get(mask, 0);
 | |
|                 if (is_absolute)
 | |
|                     dx -= device->last.valuators[0];
 | |
|                 else if (valuator_mask_has_unaccelerated(mask))
 | |
|                     dx = valuator_mask_get_unaccelerated(mask, 0);
 | |
|             }
 | |
| 
 | |
|             if (valuator_mask_isset(mask, 1)) {
 | |
|                 dy = valuator_mask_get(mask, 1);
 | |
|                 if (is_absolute)
 | |
|                     dy -= device->last.valuators[1];
 | |
|                 else if (valuator_mask_has_unaccelerated(mask))
 | |
|                     dy = valuator_mask_get_unaccelerated(mask, 1);
 | |
|             }
 | |
| 
 | |
|             if (DGAStealMotionEvent(device, idx, dx, dy))
 | |
|                 stolen = 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     for (i = 2; i < valuator_mask_size(mask); i++) {
 | |
|         AxisInfoPtr ax;
 | |
|         double incr;
 | |
|         int val, button;
 | |
| 
 | |
|         if (i >= device->valuator->numAxes)
 | |
|             break;
 | |
| 
 | |
|         if (!valuator_mask_isset(mask, i))
 | |
|             continue;
 | |
| 
 | |
|         ax = &device->valuator->axes[i];
 | |
| 
 | |
|         if (ax->scroll.type == SCROLL_TYPE_NONE)
 | |
|             continue;
 | |
| 
 | |
|         if (!scr) {
 | |
|             scr = miPointerGetScreen(device);
 | |
|             if (!scr)
 | |
|                 break;
 | |
|             idx = scr->myNum;
 | |
|         }
 | |
| 
 | |
|         incr = ax->scroll.increment;
 | |
|         val = valuator_mask_get(mask, i);
 | |
| 
 | |
|         if (ax->scroll.type == SCROLL_TYPE_VERTICAL) {
 | |
|             if (incr * val < 0)
 | |
|                 button = 4; /* up */
 | |
|             else
 | |
|                 button = 5; /* down */
 | |
|         } else { /* SCROLL_TYPE_HORIZONTAL */
 | |
|             if (incr * val < 0)
 | |
|                 button = 6; /* left */
 | |
|             else
 | |
|                 button = 7; /* right */
 | |
|         }
 | |
| 
 | |
|         if (DGAStealButtonEvent(device, idx, button, 1) &&
 | |
|                 DGAStealButtonEvent(device, idx, button, 0))
 | |
|             stolen = 1;
 | |
|     }
 | |
| 
 | |
| #endif
 | |
| 
 | |
|     return stolen;
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostMotionEventM(DeviceIntPtr device,
 | |
|                      int is_absolute, const ValuatorMask *mask)
 | |
| {
 | |
|     int flags = 0;
 | |
| 
 | |
|     if (xf86CheckMotionEvent4DGA(device, is_absolute, mask))
 | |
|         return;
 | |
| 
 | |
|     if (valuator_mask_num_valuators(mask) > 0) {
 | |
|         if (is_absolute)
 | |
|             flags = POINTER_ABSOLUTE;
 | |
|         else
 | |
|             flags = POINTER_RELATIVE | POINTER_ACCELERATE;
 | |
|     }
 | |
| 
 | |
|     QueuePointerEvents(device, MotionNotify, 0, flags, mask);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostProximityEvent(DeviceIntPtr device,
 | |
|                        int is_in, int first_valuator, int num_valuators, ...)
 | |
| {
 | |
|     va_list var;
 | |
|     int i;
 | |
|     ValuatorMask mask;
 | |
| 
 | |
|     XI_VERIFY_VALUATORS(num_valuators);
 | |
| 
 | |
|     valuator_mask_zero(&mask);
 | |
|     va_start(var, num_valuators);
 | |
|     for (i = 0; i < num_valuators; i++)
 | |
|         valuator_mask_set(&mask, first_valuator + i, va_arg(var, int));
 | |
| 
 | |
|     va_end(var);
 | |
| 
 | |
|     xf86PostProximityEventM(device, is_in, &mask);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostProximityEventP(DeviceIntPtr device,
 | |
|                         int is_in,
 | |
|                         int first_valuator,
 | |
|                         int num_valuators, const int *valuators)
 | |
| {
 | |
|     ValuatorMask mask;
 | |
| 
 | |
|     XI_VERIFY_VALUATORS(num_valuators);
 | |
| 
 | |
|     valuator_mask_set_range(&mask, first_valuator, num_valuators, valuators);
 | |
|     xf86PostProximityEventM(device, is_in, &mask);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostProximityEventM(DeviceIntPtr device,
 | |
|                         int is_in, const ValuatorMask *mask)
 | |
| {
 | |
|     QueueProximityEvents(device, is_in ? ProximityIn : ProximityOut, mask);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostButtonEvent(DeviceIntPtr device,
 | |
|                     int is_absolute,
 | |
|                     int button,
 | |
|                     int is_down, int first_valuator, int num_valuators, ...)
 | |
| {
 | |
|     va_list var;
 | |
|     ValuatorMask mask;
 | |
|     int i = 0;
 | |
| 
 | |
|     XI_VERIFY_VALUATORS(num_valuators);
 | |
| 
 | |
|     valuator_mask_zero(&mask);
 | |
| 
 | |
|     va_start(var, num_valuators);
 | |
|     for (i = 0; i < num_valuators; i++)
 | |
|         valuator_mask_set(&mask, first_valuator + i, va_arg(var, int));
 | |
| 
 | |
|     va_end(var);
 | |
| 
 | |
|     xf86PostButtonEventM(device, is_absolute, button, is_down, &mask);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostButtonEventP(DeviceIntPtr device,
 | |
|                      int is_absolute,
 | |
|                      int button,
 | |
|                      int is_down,
 | |
|                      int first_valuator,
 | |
|                      int num_valuators, const int *valuators)
 | |
| {
 | |
|     ValuatorMask mask;
 | |
| 
 | |
|     XI_VERIFY_VALUATORS(num_valuators);
 | |
| 
 | |
|     valuator_mask_set_range(&mask, first_valuator, num_valuators, valuators);
 | |
|     xf86PostButtonEventM(device, is_absolute, button, is_down, &mask);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostButtonEventM(DeviceIntPtr device,
 | |
|                      int is_absolute,
 | |
|                      int button, int is_down, const ValuatorMask *mask)
 | |
| {
 | |
|     int flags = 0;
 | |
| 
 | |
|     if (valuator_mask_num_valuators(mask) > 0) {
 | |
|         if (is_absolute)
 | |
|             flags = POINTER_ABSOLUTE;
 | |
|         else
 | |
|             flags = POINTER_RELATIVE | POINTER_ACCELERATE;
 | |
|     }
 | |
| 
 | |
| #ifdef XFreeXDGA
 | |
|     if (miPointerGetScreen(device)) {
 | |
|         int index = miPointerGetScreen(device)->myNum;
 | |
| 
 | |
|         if (DGAStealButtonEvent(device, index, button, is_down))
 | |
|             return;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     QueuePointerEvents(device,
 | |
|                        is_down ? ButtonPress : ButtonRelease, button,
 | |
|                        flags, mask);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostKeyEvent(DeviceIntPtr device, unsigned int key_code, int is_down)
 | |
| {
 | |
|     xf86PostKeyEventM(device, key_code, is_down);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostKeyEventP(DeviceIntPtr device,
 | |
|                   unsigned int key_code,
 | |
|                   int is_down)
 | |
| {
 | |
|     xf86PostKeyEventM(device, key_code, is_down);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostKeyEventM(DeviceIntPtr device, unsigned int key_code, int is_down)
 | |
| {
 | |
| #ifdef XFreeXDGA
 | |
|     DeviceIntPtr pointer;
 | |
| 
 | |
|     /* Some pointers send key events, paired device is wrong then. */
 | |
|     pointer = GetMaster(device, POINTER_OR_FLOAT);
 | |
| 
 | |
|     if (miPointerGetScreen(pointer)) {
 | |
|         int index = miPointerGetScreen(pointer)->myNum;
 | |
| 
 | |
|         if (DGAStealKeyEvent(device, index, key_code, is_down))
 | |
|             return;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     QueueKeyboardEvents(device, is_down ? KeyPress : KeyRelease, key_code);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86PostKeyboardEvent(DeviceIntPtr device, unsigned int key_code, int is_down)
 | |
| {
 | |
|     ValuatorMask mask;
 | |
| 
 | |
|     valuator_mask_zero(&mask);
 | |
|     xf86PostKeyEventM(device, key_code, is_down);
 | |
| }
 | |
| 
 | |
| InputInfoPtr
 | |
| xf86FirstLocalDevice(void)
 | |
| {
 | |
|     return xf86InputDevs;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Cx     - raw data from touch screen
 | |
|  * to_max - scaled highest dimension
 | |
|  *          (remember, this is of rows - 1 because of 0 origin)
 | |
|  * to_min  - scaled lowest dimension
 | |
|  * from_max - highest raw value from touch screen calibration
 | |
|  * from_min  - lowest raw value from touch screen calibration
 | |
|  *
 | |
|  * This function is the same for X or Y coordinates.
 | |
|  * You may have to reverse the high and low values to compensate for
 | |
|  * different origins on the touch screen vs X.
 | |
|  *
 | |
|  * e.g. to scale from device coordinates into screen coordinates, call
 | |
|  * xf86ScaleAxis(x, 0, screen_width, dev_min, dev_max);
 | |
|  */
 | |
| 
 | |
| int
 | |
| xf86ScaleAxis(int Cx, int to_max, int to_min, int from_max, int from_min)
 | |
| {
 | |
|     int X;
 | |
|     int64_t to_width = to_max - to_min;
 | |
|     int64_t from_width = from_max - from_min;
 | |
| 
 | |
|     if (from_width) {
 | |
|         X = (int) (((to_width * (Cx - from_min)) / from_width) + to_min);
 | |
|     }
 | |
|     else {
 | |
|         X = 0;
 | |
|         ErrorF("Divide by Zero in xf86ScaleAxis\n");
 | |
|     }
 | |
| 
 | |
|     if (X > to_max)
 | |
|         X = to_max;
 | |
|     if (X < to_min)
 | |
|         X = to_min;
 | |
| 
 | |
|     return X;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
 | |
|                            int maxval, int resolution, int min_res, int max_res,
 | |
|                            int mode)
 | |
| {
 | |
|     if (!dev || !dev->valuator)
 | |
|         return FALSE;
 | |
| 
 | |
|     return InitValuatorAxisStruct(dev, axnum, label, minval, maxval, resolution,
 | |
|                                   min_res, max_res, mode);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Set the valuator values to be in sync with dix/event.c
 | |
|  * DefineInitialRootWindow().
 | |
|  */
 | |
| void
 | |
| xf86InitValuatorDefaults(DeviceIntPtr dev, int axnum)
 | |
| {
 | |
|     if (axnum == 0) {
 | |
|         dev->valuator->axisVal[0] = screenInfo.screens[0]->width / 2;
 | |
|         dev->last.valuators[0] = dev->valuator->axisVal[0];
 | |
|     }
 | |
|     else if (axnum == 1) {
 | |
|         dev->valuator->axisVal[1] = screenInfo.screens[0]->height / 2;
 | |
|         dev->last.valuators[1] = dev->valuator->axisVal[1];
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Deactivate a device. Call this function from the driver if you receive a
 | |
|  * read error or something else that spoils your day.
 | |
|  * Device will be moved to the off_devices list, but it will still be there
 | |
|  * until you really clean up after it.
 | |
|  * Notifies the client about an inactive device.
 | |
|  *
 | |
|  * @param panic True if device is unrecoverable and needs to be removed.
 | |
|  */
 | |
| void
 | |
| xf86DisableDevice(DeviceIntPtr dev, Bool panic)
 | |
| {
 | |
|     if (!panic) {
 | |
|         DisableDevice(dev, TRUE);
 | |
|     }
 | |
|     else {
 | |
|         SendDevicePresenceEvent(dev->id, DeviceUnrecoverable);
 | |
|         DeleteInputDeviceRequest(dev);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Reactivate a device. Call this function from the driver if you just found
 | |
|  * out that the read error wasn't quite that bad after all.
 | |
|  * Device will be re-activated, and an event sent to the client.
 | |
|  */
 | |
| void
 | |
| xf86EnableDevice(DeviceIntPtr dev)
 | |
| {
 | |
|     EnableDevice(dev, TRUE);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Post a touch event with optional valuators.  If this is the first touch in
 | |
|  * the sequence, at least x & y valuators must be provided. The driver is
 | |
|  * responsible for maintaining the correct event sequence (TouchBegin, TouchUpdate,
 | |
|  * TouchEnd). Submitting an update or end event for a unregistered touchid will
 | |
|  * result in errors.
 | |
|  * Touch IDs may be reused by the driver but only after a TouchEnd has been
 | |
|  * submitted for that touch ID.
 | |
|  *
 | |
|  * @param dev The device to post the event for
 | |
|  * @param touchid The touchid of the current touch event. Must be an
 | |
|  * existing ID for TouchUpdate or TouchEnd events
 | |
|  * @param type One of XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd
 | |
|  * @param flags Flags for this event
 | |
|  * @param The valuator mask with all valuators set for this event.
 | |
|  */
 | |
| void
 | |
| xf86PostTouchEvent(DeviceIntPtr dev, uint32_t touchid, uint16_t type,
 | |
|                    uint32_t flags, const ValuatorMask *mask)
 | |
| {
 | |
| 
 | |
|     QueueTouchEvents(dev, type, touchid, flags, mask);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Post a gesture pinch event.  The driver is responsible for maintaining the
 | |
|  * correct event sequence (GesturePinchBegin, GesturePinchUpdate,
 | |
|  * GesturePinchEnd).
 | |
|  *
 | |
|  * @param dev The device to post the event for
 | |
|  * @param type One of XI_GesturePinchBegin, XI_GesturePinchUpdate,
 | |
|  *        XI_GesturePinchEnd
 | |
|  * @param num_touches The number of touches in the gesture
 | |
|  * @param flags Flags for this event
 | |
|  * @param delta_x,delta_y accelerated relative motion delta
 | |
|  * @param delta_unaccel_x,delta_unaccel_y unaccelerated relative motion delta
 | |
|  * @param scale absolute scale of a pinch gesture
 | |
|  * @param delta_angle the ange delta in degrees between the last and the current pinch event.
 | |
|  */
 | |
| void
 | |
| xf86PostGesturePinchEvent(DeviceIntPtr dev, uint16_t type,
 | |
|                           uint16_t num_touches, uint32_t flags,
 | |
|                           double delta_x, double delta_y,
 | |
|                           double delta_unaccel_x,
 | |
|                           double delta_unaccel_y,
 | |
|                           double scale, double delta_angle)
 | |
| {
 | |
|     QueueGesturePinchEvents(dev, type, num_touches, flags, delta_x, delta_y,
 | |
|                             delta_unaccel_x, delta_unaccel_y,
 | |
|                             scale, delta_angle);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Post a gesture swipe event.  The driver is responsible for maintaining the
 | |
|  * correct event sequence (GestureSwipeBegin, GestureSwipeUpdate,
 | |
|  * GestureSwipeEnd).
 | |
|  *
 | |
|  * @param dev The device to post the event for
 | |
|  * @param type One of XI_GestureSwipeBegin, XI_GestureSwipeUpdate,
 | |
|  *        XI_GestureSwipeEnd
 | |
|  * @param num_touches The number of touches in the gesture
 | |
|  * @param flags Flags for this event
 | |
|  * @param delta_x,delta_y accelerated relative motion delta
 | |
|  * @param delta_unaccel_x,delta_unaccel_y unaccelerated relative motion delta
 | |
|  */
 | |
| void
 | |
| xf86PostGestureSwipeEvent(DeviceIntPtr dev, uint16_t type,
 | |
|                           uint16_t num_touches, uint32_t flags,
 | |
|                           double delta_x, double delta_y,
 | |
|                           double delta_unaccel_x,
 | |
|                           double delta_unaccel_y)
 | |
| {
 | |
|     QueueGestureSwipeEvents(dev, type, num_touches, flags, delta_x, delta_y,
 | |
|                             delta_unaccel_x, delta_unaccel_y);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86InputEnableVTProbe(void)
 | |
| {
 | |
|     int is_auto = 0;
 | |
|     DeviceIntPtr pdev;
 | |
|     PausedInputDevicePtr d, tmp;
 | |
| 
 | |
|     xorg_list_for_each_entry_safe(d, tmp, &new_input_devices_list, node) {
 | |
|         InputInfoPtr pInfo = d->pInfo;
 | |
|         const char *value = xf86findOptionValue(pInfo->options, "_source");
 | |
| 
 | |
|         is_auto = 0;
 | |
|         if (value &&
 | |
|             (strcmp(value, "server/hal") == 0 ||
 | |
|              strcmp(value, "server/udev") == 0 ||
 | |
|              strcmp(value, "server/wscons") == 0))
 | |
|             is_auto = 1;
 | |
| 
 | |
|         xf86NewInputDevice(pInfo, &pdev,
 | |
|                                   (!is_auto ||
 | |
|                                    (is_auto && xf86Info.autoEnableDevices)));
 | |
|         xorg_list_del(&d->node);
 | |
|         free(d);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* end of xf86Xinput.c */
 |