diff options
author | Kibum Kim <kb0929.kim@samsung.com> | 2012-01-07 01:10:12 +0900 |
---|---|---|
committer | Kibum Kim <kb0929.kim@samsung.com> | 2012-01-07 01:10:12 +0900 |
commit | 95e6f48d7cc099b11624393e61fd3a7a21eb9885 (patch) | |
tree | aacee5de5423e04c7a9978e9014c1b2560624de0 | |
parent | f1f0f7bda8912ff69f0af55aab20ef61c562bde4 (diff) | |
download | xserver-xorg-input-evdev-95e6f48d7cc099b11624393e61fd3a7a21eb9885.tar.gz xserver-xorg-input-evdev-95e6f48d7cc099b11624393e61fd3a7a21eb9885.tar.bz2 xserver-xorg-input-evdev-95e6f48d7cc099b11624393e61fd3a7a21eb9885.zip |
Git init
-rw-r--r-- | COPYING | 95 | ||||
-rw-r--r-- | Makefile.am | 40 | ||||
-rw-r--r-- | README | 20 | ||||
-rwxr-xr-x | autogen.sh | 12 | ||||
-rw-r--r-- | configure.ac | 77 | ||||
-rw-r--r-- | debian/README.source | 73 | ||||
-rwxr-xr-x | debian/changelog | 127 | ||||
-rw-r--r-- | debian/compat | 1 | ||||
-rw-r--r-- | debian/control | 35 | ||||
-rw-r--r-- | debian/copyright | 100 | ||||
-rw-r--r-- | debian/local/10-x11-evdev.fdi | 18 | ||||
-rwxr-xr-x | debian/rules | 108 | ||||
-rw-r--r-- | debian/watch | 2 | ||||
-rw-r--r-- | debian/xserver-xorg-input-evdev.install | 2 | ||||
-rw-r--r-- | debian/xserver-xorg-input-evdev.links | 1 | ||||
-rw-r--r-- | debian/xserver-xorg-input-evdev.manpages | 1 | ||||
-rw-r--r-- | debian/xsfbs/repack.sh | 32 | ||||
-rw-r--r-- | debian/xsfbs/xsfbs.mk | 293 | ||||
-rw-r--r-- | debian/xsfbs/xsfbs.sh | 853 | ||||
-rw-r--r-- | include/Makefile.am | 2 | ||||
-rwxr-xr-x | include/evdev-properties.h | 69 | ||||
-rw-r--r-- | man/Makefile.am | 57 | ||||
-rw-r--r-- | man/evdev.man | 231 | ||||
-rw-r--r-- | packaging/xorg-x11-drv-evdev.spec | 54 | ||||
-rw-r--r-- | src/Makefile.am | 40 | ||||
-rw-r--r-- | src/draglock.c | 321 | ||||
-rw-r--r-- | src/emuMB.c | 421 | ||||
-rw-r--r-- | src/emuWheel.c | 478 | ||||
-rwxr-xr-x | src/evdev.c | 2699 | ||||
-rw-r--r-- | src/evdev.h | 225 | ||||
-rw-r--r-- | xorg-evdev.pc.in | 6 |
31 files changed, 6493 insertions, 0 deletions
@@ -0,0 +1,95 @@ +Various copyright notices found in this driver: + +Copyright © 2004-2008 Red Hat, Inc. + +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 Red Hat +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. Red +Hat makes no representations about the suitability of this software +for any purpose. It is provided "as is" without express or implied +warranty. + +THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN +NO EVENT SHALL THE AUTHORS 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 2005 Sun Microsystems, Inc. All rights reserved. + +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. + +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 OPEN GROUP 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. + +Except as contained in this notice, the name of the copyright holders 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 holders. + +Copyright © 2008 University of South Australia +copyrights taken from xf86-input-mouse, partly valid for this driver. +Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. +Copyright 1993 by David Dawes <dawes@xfree86.org> +Copyright 2002 by SuSE Linux AG, Author: Egbert Eich +Copyright 1994-2002 by The XFree86 Project, Inc. +Copyright 2002 by Paul Elliott + +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 the authors +not be used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. The authors make no +representations about the suitability of this software for any +purpose. It is provided "as is" without express or implied +warranty. + +THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN +NO EVENT SHALL THE AUTHORS 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 2005 Adam Jackson. + +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 +on the rights to use, copy, modify, merge, publish, distribute, sub +license, 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 (including the next +paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL +ADAM JACKSON 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. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..e8e6ec1 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,40 @@ +# Copyright 2005 Adam Jackson. +# +# 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 +# on the rights to use, copy, modify, merge, publish, distribute, sub +# license, 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 (including the next +# paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL +# ADAM JACKSON 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. + +AUTOMAKE_OPTIONS = foreign + +# Ensure headers are installed below $(prefix) for distcheck +DISTCHECK_CONFIGURE_FLAGS = --with-sdkdir='$${includedir}/xorg' + +SUBDIRS = src man include + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = xorg-evdev.pc + +EXTRA_DIST = ChangeLog + +MAINTAINERCLEANFILES=ChangeLog + +.PHONY: ChangeLog + +ChangeLog: + $(CHANGELOG_CMD) + +dist-hook: ChangeLog @@ -0,0 +1,20 @@ +xf86-input-evdev - Generic Linux input driver for the Xorg X server + +Please submit bugs & patches to the Xorg bugzilla: + + https://bugs.freedesktop.org/enter_bug.cgi?product=xorg + +All questions regarding this software should be directed at the +Xorg mailing list: + + http://lists.freedesktop.org/mailman/listinfo/xorg + +The master development code repository can be found at: + + git://anongit.freedesktop.org/git/xorg/driver/xf86-input-evdev + + http://cgit.freedesktop.org/xorg/driver/xf86-input-evdev + +For more information on the git code manager, see: + + http://wiki.x.org/wiki/GitPage diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..218197d --- /dev/null +++ b/autogen.sh @@ -0,0 +1,12 @@ +#! /bin/sh + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir + +autoreconf -v --install || exit 1 +cd $ORIGDIR || exit $? + +#$srcdir/configure --enable-maintainer-mode "$@" diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..d2a3bc1 --- /dev/null +++ b/configure.ac @@ -0,0 +1,77 @@ +# Copyright 2005 Adam Jackson. +# +# 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 +# on the rights to use, copy, modify, merge, publish, distribute, sub +# license, 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 (including the next +# paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL +# ADAM JACKSON 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. +# +# Process this file with autoconf to produce a configure script + +AC_PREREQ(2.57) +AC_INIT([xf86-input-evdev], + 2.3.2, + [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg], + xf86-input-evdev) + +AC_CONFIG_SRCDIR([Makefile.am]) +AC_CONFIG_AUX_DIR(.) +AM_INIT_AUTOMAKE([dist-bzip2]) + +AM_MAINTAINER_MODE + +# Require xorg-macros: XORG_DEFAULT_OPTIONS +m4_ifndef([XORG_MACROS_VERSION], [AC_FATAL([must install xorg-macros 1.3 or later before running autoconf/autogen])]) +XORG_MACROS_VERSION(1.3) +AM_CONFIG_HEADER([config.h]) + +# Checks for programs. +AC_DISABLE_STATIC +AC_PROG_LIBTOOL +AC_PROG_CC +XORG_DEFAULT_OPTIONS + +AH_TOP([#include "xorg-server.h"]) + +AC_ARG_WITH(xorg-module-dir, + AC_HELP_STRING([--with-xorg-module-dir=DIR], + [Default xorg module directory [[default=$libdir/xorg/modules]]]), + [moduledir="$withval"], + [moduledir="$libdir/xorg/modules"]) +inputdir=${moduledir}/input +AC_SUBST(inputdir) + +# Checks for pkg-config packages. We need to be able to override sdkdir +# to satisfy silly distcheck requirements. +PKG_CHECK_MODULES(XORG, xorg-server xproto $REQUIRED_MODULES) +XORG_CFLAGS="$CWARNFLAGS $XORG_CFLAGS" +AC_ARG_WITH([sdkdir], [], + [sdkdir="$withval"], + [sdkdir=`$PKG_CONFIG --variable=sdkdir xorg-server`]) +AC_SUBST([sdkdir]) + +# Checks for libraries. + +# Checks for header files. +AC_HEADER_STDC + +DRIVER_NAME=evdev +AC_SUBST([DRIVER_NAME]) + +AC_OUTPUT([Makefile + src/Makefile + man/Makefile + include/Makefile + xorg-evdev.pc]) diff --git a/debian/README.source b/debian/README.source new file mode 100644 index 0000000..34ab4bf --- /dev/null +++ b/debian/README.source @@ -0,0 +1,73 @@ +------------------------------------------------------ +Quick Guide To Patching This Package For The Impatient +------------------------------------------------------ + +1. Make sure you have quilt installed +2. Unpack the package as usual with "dpkg-source -x" +3. Run the "patch" target in debian/rules +4. Create a new patch with "quilt new" (see quilt(1)) +5. Edit all the files you want to include in the patch with "quilt edit" + (see quilt(1)). +6. Write the patch with "quilt refresh" (see quilt(1)) +7. Run the "clean" target in debian/rules + +Alternatively, instead of using quilt directly, you can drop the patch in to +debian/patches and add the name of the patch to debian/patches/series. + +------------------------------------ +Guide To The X Strike Force Packages +------------------------------------ + +The X Strike Force team maintains X packages in git repositories on +git.debian.org in the pkg-xorg subdirectory. Most upstream packages +are actually maintained in git repositories as well, so they often +just need to be pulled into git.debian.org in a "upstream-*" branch. +Otherwise, the upstream sources are manually installed in the Debian +git repository. + +The .orig.tar.gz upstream source file could be generated this +"upstream-*" branch in the Debian git repository but it is actually +copied from upstream tarballs directly. + +Due to X.org being highly modular, packaging all X.org applications +as their own independent packages would have created too many Debian +packages. For this reason, some X.org applications have been grouped +into larger packages: xutils, xutils-dev, x11-apps, x11-session-utils, +x11-utils, x11-xfs-utils, x11-xkb-utils, x11-xserver-utils. +Most packages, including the X.org server itself and all libraries +and drivers are, however maintained independently. + +The Debian packaging is added by creating the "debian-*" git branch +which contains the aforementioned "upstream-*" branch plus the debian/ +repository files. +When a patch has to be applied to the Debian package, two solutions +are involved: +* If the patch is available in one of the upstream branches, it + may be git'cherry-picked into the Debian repository. In this + case, it appears directly in the .diff.gz. +* Otherwise, the patch is added to debian/patches/ which is managed + with quilt as documented in /usr/share/doc/quilt/README.source. + +quilt is actually invoked by the Debian X packaging through a larger +set of scripts called XSFBS. XSFBS brings some other X specific +features such as managing dependencies and conflicts due to the video +and input driver ABIs. +XSFBS itself is maintained in a separate repository at + git://git.debian.org/pkg-xorg/xsfbs.git +and it is pulled inside the other Debian X repositories when needed. + +The XSFBS patching system requires a build dependency on quilt. Also +a dependency on $(STAMP_DIR)/patch has to be added to debian/rules +so that the XSFBS patching occurs before the actual build. So the +very first target of the build (likely the one running autoreconf) +should depend on $(STAMP_DIR)/patch. It should also not depend on +anything so that parallel builds are correctly supported (nothing +should probably run while patching is being done). And finally, the +clean target should depend on the xsfclean target so that patches +are unapplied on clean. + +When the upstream sources contain some DFSG-nonfree files, they are +listed in text files in debian/prune/ in the "debian-*" branch of +the Debian repository. XSFBS' scripts then take care of removing +these listed files during the build so as to generate a modified +DFSG-free .orig.tar.gz tarball. diff --git a/debian/changelog b/debian/changelog new file mode 100755 index 0000000..fbe620d --- /dev/null +++ b/debian/changelog @@ -0,0 +1,127 @@ +xserver-xorg-input-evdev (2.3.2-4slp2+13) unstable; urgency=low + + * Remove log messages applied in previous version + * Git: 165.213.180.234:slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+13 + + -- Sung-Jin Park <sj76.park@samsung.com> Thu, 17 Nov 2011 13:07:31 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+12) unstable; urgency=low + + * Leave log messages when a key is pressed or released. + * Git: 165.213.180.234:slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+12 + + -- Sung-Jin Park <sj76.park@samsung.com> Fri, 04 Nov 2011 17:42:48 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+11) unstable; urgency=low + + * oal rollback + * Git: 165.213.180.234:slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+11 + + -- SooChan Lim <sc1.lim@samsung.com> Fri, 08 Apr 2011 13:52:53 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+10) unstable; urgency=low + + * Work for oal + * Git: 165.213.180.234:slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+10 + + -- SooChan Lim <sc1.lim@samsung.com> Tue, 05 Apr 2011 10:29:41 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+9) unstable; urgency=low + + * [X11R7.6] upgrade package + * Git: 165.213.180.234:slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+9 + + -- SooChan Lim <sc1.lim@samsung.com> Thu, 03 Mar 2011 19:34:31 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+8) unstable; urgency=low + + * Remove properties and functions related to set/unset the existence of + mouse and external keyboard devices + * Git: 165.213.180.234:slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+8 + + -- Sung-Jin Park <sj76.park@samsung.com> Tue, 01 Feb 2011 13:48:06 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+7) unstable; urgency=low + + * Add functions setting external keyboard existence + * Increase/decrease mouse existence when a mouse is connected/disconnected + * Remove a macro _F_X_MOUSE_EXIST_ + * Git: 165.213.180.234:slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+7 + + -- Sung-Jin Park <sj76.park@samsung.com> Mon, 03 Jan 2011 19:31:10 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+6) unstable; urgency=low + + * Add xserver-xorg-input-evdev-dbg package into debian/control file + * Modify debian/rules to strip binary except xserver-xorg-input-evdev-dbg + * Git: 165.213.180.234:/git/slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+6 + + -- Sung-Jin Park <sj76.park@samsung.com> Mon, 22 Nov 2010 13:55:15 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+5) unstable; urgency=low + + * Apply --as-needed option + * Git: 165.213.180.234:/git/slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+5 + + -- Sung-Jin Park <sj76.park@samsung.com> Sat, 20 Nov 2010 17:06:01 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+4) unstable; urgency=low + + * Change macro name (from _EVDEV_INIT_ABS_ONLY_FOR_POINTER_ to + _F_INIT_ABS_ONLY_FOR_POINTER_) + * Add a property for setting mouse existence (_F_X_MOUSE_EXIST_ macro) + * Git: 165.213.180.234:/git/slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+4 + + -- Sung-Jin Park <sj76.park@samsung.com> Fri, 19 Nov 2010 15:17:25 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+3) unstable; urgency=low + + * Modify evdev.c not to initialize abs class for keyboard device + * Git: 165.213.180.234:/git/slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+3 + + -- Sung-Jin Park <sj76.park@samsung.com> Thu, 18 Nov 2010 13:37:22 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+2) unstable; urgency=low + + * Reupload for fixing the problem (ABI not matching with xorg-server) + * Git: 165.213.180.234:/git/slp/pkgs/xorg/driver/xserver-xorg-input-evdev + * Tag: xserver-xorg-input-evdev_2.3.2-4slp2+2 + + -- Sung-Jin Park <sj76.park@samsung.com> Wed, 10 Nov 2010 20:08:54 +0900 + +xserver-xorg-input-evdev (2.3.2-4slp2+1) unstable; urgency=low + + * Change revision + + -- Sung-Jin Park <sj76.park@samsung.com> Thu, 25 Mar 2010 18:08:01 +0900 + +xserver-xorg-input-evdev (2.3.2-3) unstable; urgency=low + + * Add EvdevSwapAxes() function for adjusting touch screen size properly + when screen is rotated with xrandr extension + (Adjust absinfo of max_x, min_x, max_y, min_y) + + -- Sung-Jin Park <sj76.park@samsung.com> Fri, 19 Mar 2010 17:51:14 +0900 + +xserver-xorg-input-evdev (2.3.2-2) unstable; urgency=low + + * Change not to strip binary + + -- Sung-Jin Park <sj76.park@samsung.com> Thu, 04 Feb 2010 17:00:32 +0900 + +xserver-xorg-input-evdev (2.3.2-1) unstable; urgency=low + + * Initial release + + -- Sung-Jin Park <sj76.park@samsung.com> Tue, 29 Sep 2009 21:04:26 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..637d384 --- /dev/null +++ b/debian/control @@ -0,0 +1,35 @@ +Source: xserver-xorg-input-evdev +Section: x11 +Priority: optional +Maintainer: Sung-Jin Park <sj76.park@samsung.com> +#Maintainer: Debian X Strike Force <debian-x@lists.debian.org> +Uploaders: SooChan Lim <sc1.lim@samsung.com>, Sung-Jin Park <sj76.park@samsung.com> +#Uploaders: David Nusinow <dnusinow@debian.org>, Drew Parsons <dparsons@debian.org>, Julien Cristau <jcristau@debian.org>, Brice Goglin <bgoglin@debian.org> +Build-Depends: debhelper (>= 5.0.0), pkg-config, quilt, xserver-xorg-dev (>= 1.6.99.900), x11proto-core-dev, x11proto-randr-dev, x11proto-input-dev, x11proto-kb-dev, libxkbfile-dev, dpkg-dev (>= 1.14.17), automake, libtool, xutils-dev (>= 1:7.3~1) +Standards-Version: 3.8.3 +Vcs-Git: git://git.debian.org/git/pkg-xorg/driver/xserver-xorg-input-evdev +Vcs-Browser: http://git.debian.org/?p=pkg-xorg/driver/xserver-xorg-input-evdev.git + +Package: xserver-xorg-input-evdev +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, ${xserver} +Provides: ${xinpdriver:Provides} +Replaces: xserver-xorg (<< 6.8.2-35) +Description: X.Org X server -- evdev input driver + This package provides the driver for input devices using evdev, the Linux + kernel's event delivery mechanism. This driver allows for multiple keyboards + and mice to be treated as separate input devices. + . + More information about X.Org can be found at: + <URL:http://www.X.org> + <URL:http://xorg.freedesktop.org> + <URL:http://lists.freedesktop.org/mailman/listinfo/xorg> + . + This package is built from the X.org xf86-input-evdev driver module. + +Package: xserver-xorg-input-evdev-dbg +Section: debug +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, ${xserver}, xserver-xorg-input-evdev (=${Source-Version}) +Description: Debug package of xserver-xorg-input-evdev driver + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..4cf9803 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,100 @@ +This package was downloaded from +git://anongit.freedesktop.org/git/xorg/driver/xf86-input-evdev. +Release tarballs are available at +http://xorg.freedesktop.org/releases/individual/driver/ + +Various copyright notices found in this driver: + +Copyright © 2004-2008 Red Hat, Inc. + +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 Red Hat +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. Red +Hat makes no representations about the suitability of this software +for any purpose. It is provided "as is" without express or implied +warranty. + +THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN +NO EVENT SHALL THE AUTHORS 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 2005 Sun Microsystems, Inc. All rights reserved. + +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. + +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 OPEN GROUP 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. + +Except as contained in this notice, the name of the copyright holders 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 holders. + +Copyright © 2008 University of South Australia +copyrights taken from xf86-input-mouse, partly valid for this driver. +Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. +Copyright 1993 by David Dawes <dawes@xfree86.org> +Copyright 2002 by SuSE Linux AG, Author: Egbert Eich +Copyright 1994-2002 by The XFree86 Project, Inc. +Copyright 2002 by Paul Elliott + +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 the authors +not be used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. The authors make no +representations about the suitability of this software for any +purpose. It is provided "as is" without express or implied +warranty. + +THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN +NO EVENT SHALL THE AUTHORS 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 2005 Adam Jackson. + +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 +on the rights to use, copy, modify, merge, publish, distribute, sub +license, 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 (including the next +paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL +ADAM JACKSON 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. + diff --git a/debian/local/10-x11-evdev.fdi b/debian/local/10-x11-evdev.fdi new file mode 100644 index 0000000..a7fe8cc --- /dev/null +++ b/debian/local/10-x11-evdev.fdi @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<deviceinfo version="0.2"> + <device> + <match key="info.capabilities" contains="input.mouse"> + <match key="/org/freedesktop/Hal/devices/computer:system.kernel.name" + string="Linux"> + <merge key="input.x11_driver" type="string">evdev</merge> + </match> + </match> + + <match key="info.capabilities" contains="input.keys"> + <match key="/org/freedesktop/Hal/devices/computer:system.kernel.name" + string="Linux"> + <merge key="input.x11_driver" type="string">evdev</merge> + </match> + </match> + </device> +</deviceinfo> diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..360a704 --- /dev/null +++ b/debian/rules @@ -0,0 +1,108 @@ +#!/usr/bin/make -f +# debian/rules for the Debian xserver-xorg-input-evdev package. +# Copyright © 2004 Scott James Remnant <scott@netsplit.com> +# Copyright © 2005 Daniel Stone <daniel@fooishbar.org> +# Copyright © 2005 David Nusinow <dnusinow@debian.org> + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +include debian/xsfbs/xsfbs.mk + +CFLAGS = -Wall -g -D_F_INIT_ABS_ONLY_FOR_POINTER_ +LDFLAGS += -Wl,--hash-style=both -Wl,--as-needed +ifneq (,$(filter noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif +ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) + NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) + MAKEFLAGS += -j$(NUMJOBS) +endif + +DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) +ifeq ($(DEB_BUILD_GNU_TYPE), $(DEB_HOST_GNU_TYPE)) + confflags += --build=$(DEB_HOST_GNU_TYPE) +else + confflags += --build=$(DEB_HOST_GNU_TYPE) --host=$(DEB_HOST_GNU_TYPE) +# confflags += --build=$(DEB_BUILD_GNU_TYPE) --host=$(DEB_HOST_GNU_TYPE) +endif + +# kbd_drv.a isn't phenomenally useful; kbd_drv.so more so +confflags += --disable-static + +#configure: $(STAMP_DIR)/patch +configure: + autoreconf -vfi + #./autogen.sh + +obj-$(DEB_BUILD_GNU_TYPE)/config.status: configure + mkdir -p obj-$(DEB_BUILD_GNU_TYPE) + cd obj-$(DEB_BUILD_GNU_TYPE) && \ + ../configure --prefix=/usr --mandir=\$${prefix}/share/man \ + --infodir=\$${prefix}/share/info $(confflags) \ + CFLAGS="$(CFLAGS)" + +build: build-stamp +build-stamp: obj-$(DEB_BUILD_GNU_TYPE)/config.status + dh_testdir + cd obj-$(DEB_BUILD_GNU_TYPE) && $(MAKE) + >$@ + +clean: xsfclean + dh_testdir + dh_testroot + rm -f build-stamp + + rm -f config.cache config.log config.status + rm -f */config.cache */config.log */config.status + rm -f conftest* */conftest* + rm -rf autom4te.cache */autom4te.cache + rm -rf obj-* + rm -f $$(find -name Makefile.in) + rm -f compile config.guess config.sub configure depcomp install-sh + rm -f ltmain.sh missing INSTALL aclocal.m4 ylwrap mkinstalldirs config.h.in + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + cd obj-$(DEB_BUILD_GNU_TYPE) && $(MAKE) DESTDIR=$(CURDIR)/debian/tmp install + +# Build architecture-dependent files here. +binary-arch: build install serverabi + dh_testdir + dh_testroot + + dh_installdocs +# dh_installchangelogs ChangeLog + dh_installchangelogs + dh_install --sourcedir=debian/tmp --list-missing --exclude=usr/share/man/man4 + #dh_install --sourcedir=debian/tmp --list-missing --exclude=evdev_drv.la --exclude=usr/share/man/man4 +# install -d debian/xserver-xorg-input-evdev/usr/share/hal/fdi/policy/20thirdparty +# install -m 644 debian/local/10-x11-evdev.fdi debian/xserver-xorg-input-evdev/usr/share/hal/fdi/policy/20thirdparty/ + dh_installman + dh_link + dh_strip --dbg-package=xserver-xorg-input-evdev-dbg + dh_compress + dh_fixperms + dh_installdeb +# dh_shlibdeps -- --warnings=6 + dh_shlibdeps -- + dh_gencontrol + dh_md5sums + dh_builddeb + +# Build architecture-independent files here. +binary-indep: build install +# Nothing to do + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..16cd015 --- /dev/null +++ b/debian/watch @@ -0,0 +1,2 @@ +version=3 +http://xorg.freedesktop.org/releases/individual/driver/ xf86-input-evdev-(.*)\.tar\.gz diff --git a/debian/xserver-xorg-input-evdev.install b/debian/xserver-xorg-input-evdev.install new file mode 100644 index 0000000..1002d70 --- /dev/null +++ b/debian/xserver-xorg-input-evdev.install @@ -0,0 +1,2 @@ +usr/lib/xorg/modules/input/*.so +usr/lib/xorg/modules/input/*.la diff --git a/debian/xserver-xorg-input-evdev.links b/debian/xserver-xorg-input-evdev.links new file mode 100644 index 0000000..c3758c4 --- /dev/null +++ b/debian/xserver-xorg-input-evdev.links @@ -0,0 +1 @@ +usr/share/bug/xserver-xorg-core/script usr/share/bug/xserver-xorg-input-evdev/script diff --git a/debian/xserver-xorg-input-evdev.manpages b/debian/xserver-xorg-input-evdev.manpages new file mode 100644 index 0000000..603f9bd --- /dev/null +++ b/debian/xserver-xorg-input-evdev.manpages @@ -0,0 +1 @@ +debian/tmp/usr/share/man/man4/* diff --git a/debian/xsfbs/repack.sh b/debian/xsfbs/repack.sh new file mode 100644 index 0000000..5935cc9 --- /dev/null +++ b/debian/xsfbs/repack.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +set -e + +if ! [ -d debian/prune ]; then + exit 0 +fi + +if [ "x$1" != x--upstream-version ]; then + exit 1 +fi + +version="$2" +filename="$3" + +if [ -z "$version" ] || ! [ -f "$filename" ]; then + exit 1 +fi + +dir="$(pwd)" +tempdir="$(mktemp -d)" + +cd "$tempdir" +tar xf "$dir/$filename" +cat "$dir"/debian/prune/* | while read file; do rm -f */$file; done + +tar czf "$dir/$filename" * +cd "$dir" +rm -rf "$tempdir" +echo "Done pruning upstream tarball" + +exit 0 diff --git a/debian/xsfbs/xsfbs.mk b/debian/xsfbs/xsfbs.mk new file mode 100644 index 0000000..f0f8953 --- /dev/null +++ b/debian/xsfbs/xsfbs.mk @@ -0,0 +1,293 @@ +#!/usr/bin/make -f + +# Debian X Strike Force Build System (XSFBS): Make portion + +# Copyright 1996 Stephen Early +# Copyright 1997 Mark Eichin +# Copyright 1998-2005, 2007 Branden Robinson +# Copyright 2005 David Nusinow +# +# Licensed under the GNU General Public License, version 2. See the file +# /usr/share/common-licenses/GPL or <http://www.gnu.org/copyleft/gpl.txt>. + +# Originally by Stephen Early <sde1000@debian.org> +# Modified by Mark W. Eichin <eichin@kitten.gen.ma.us> +# Modified by Adam Heath <doogie@debian.org> +# Modified by Branden Robinson <branden@debian.org> +# Modified by Fabio Massimo Di Nitto <fabbione@fabbione.net> +# Modified by David Nusinow <dnusinow@debian.org> +# Acknowledgements to Manoj Srivastava. + +# Pass $(DH_OPTIONS) into the environment for debhelper's benefit. +export DH_OPTIONS + +# force quilt to not use ~/.quiltrc and to use debian/patches +QUILT = QUILT_PATCHES=debian/patches quilt --quiltrc /dev/null + +# Set up parameters for the upstream build environment. + +# Determine (source) package name from Debian changelog. +SOURCE_NAME:=$(shell dpkg-parsechangelog -ldebian/changelog \ + | grep '^Source:' | awk '{print $$2}') + +# Determine package version from Debian changelog. +SOURCE_VERSION:=$(shell dpkg-parsechangelog -ldebian/changelog \ + | grep '^Version:' | awk '{print $$2}') + +# Determine upstream version number. +UPSTREAM_VERSION:=$(shell echo $(SOURCE_VERSION) | sed 's/-.*//') + +# Determine the source version without the epoch for make-orig-tar-gz +NO_EPOCH_VER:=$(shell echo $(UPSTREAM_VERSION) | sed 's/^.://') + +# Figure out who's building this package. +BUILDER:=$(shell echo $${DEBEMAIL:-$${EMAIL:-$$(echo $$LOGNAME@$$(cat /etc/mailname 2>/dev/null))}}) + +# Find out if this is an official build; an official build has nothing but +# digits, dots, and/or the codename of a release in the Debian part of the +# version number. Anything else indicates an unofficial build. +OFFICIAL_BUILD:=$(shell VERSION=$(SOURCE_VERSION); if ! expr "$$(echo $${VERSION\#\#*-} | sed 's/\(woody\|sarge\|etch\|lenny\)//g')" : ".*[^0-9.].*" >/dev/null 2>&1; then echo yes; fi) + +# Set up parameters for the Debian build environment. + +# Determine our architecture. +BUILD_ARCH:=$(shell dpkg-architecture -qDEB_BUILD_ARCH) +# Work around some old-time dpkg braindamage. +BUILD_ARCH:=$(subst i486,i386,$(BUILD_ARCH)) +# The DEB_HOST_ARCH variable may be set per the Debian cross-compilation policy. +ifdef DEB_HOST_ARCH + ARCH:=$(DEB_HOST_ARCH) +else + # dpkg-cross sets the ARCH environment variable; if set, use it. + ifdef ARCH + ARCH:=$(ARCH) + else + ARCH:=$(BUILD_ARCH) + endif +endif + +# $(STAMP_DIR) houses stamp files for complex targets. +STAMP_DIR:=stampdir + +# $(DEBTREEDIR) is where all install rules are told (via $(DESTDIR)) to place +# their files. +DEBTREEDIR:=$(CURDIR)/debian/tmp + +# All "important" targets have four lines: +# 1) A target name that is invoked by a package-building tool or the user. +# This consists of a dependency on a "$(STAMP_DIR)/"-prefixed counterpart. +# 2) A line delcaring 1) as a phony target (".PHONY:"). +# 3) A "$(STAMP_DIR)/"-prefixed target which does the actual work, and may +# depend on other targets. +# 4) A line declaring 3) as a member of the $(stampdir_targets) variable; the +# "$(STAMP_DIR)/" prefix is omitted. +# +# This indirection is needed so that the "stamp" files that signify when a rule +# is done can be located in a separate "stampdir". Recall that make has no way +# to know when a goal has been met for a phony target (like "build" or +# "install"). +# +# At the end of each "$(STAMP_DIR)/" target, be sure to run the command ">$@" +# so that the target will not be run again. Removing the file will make Make +# run the target over. + +# All phony targets should be declared as dependencies of .PHONY, even if they +# do not have "($STAMP_DIR)/"-prefixed counterparts. + +# Define a harmless default rule to keep things from going nuts by accident. +.PHONY: default +default: + +# Set up the $(STAMP_DIR) directory. +.PHONY: stampdir +stampdir_targets+=stampdir +stampdir: $(STAMP_DIR)/stampdir +$(STAMP_DIR)/stampdir: + mkdir $(STAMP_DIR) + >$@ + +# Set up the package build directory as quilt expects to find it. +.PHONY: prepare +stampdir_targets+=prepare +prepare: $(STAMP_DIR)/prepare +$(STAMP_DIR)/prepare: $(STAMP_DIR)/log $(STAMP_DIR)/genscripts + >$@ + +.PHONY: log +stampdir_targets+=log +log: $(STAMP_DIR)/log +$(STAMP_DIR)/log: $(STAMP_DIR)/stampdir + mkdir -p $(STAMP_DIR)/log + +# Apply all patches to the upstream source. +.PHONY: patch +stampdir_targets+=patch +patch: $(STAMP_DIR)/patch +$(STAMP_DIR)/patch: $(STAMP_DIR)/prepare + if ! [ `which quilt` ]; then \ + echo "Couldn't find quilt. Please install it or add it to the build-depends for this package."; \ + exit 1; \ + fi; \ + if $(QUILT) next >/dev/null 2>&1; then \ + echo -n "Applying patches..."; \ + if $(QUILT) push -a -v >$(STAMP_DIR)/log/patch 2>&1; then \ + cat $(STAMP_DIR)/log/patch; \ + echo "successful."; \ + else \ + cat $(STAMP_DIR)/log/patch; \ + echo "failed! (check $(STAMP_DIR)/log/patch for details)"; \ + exit 1; \ + fi; \ + else \ + echo "No patches to apply"; \ + fi; \ + >$@ + +# Revert all patches to the upstream source. +.PHONY: unpatch +unpatch: $(STAMP_DIR)/log + rm -f $(STAMP_DIR)/patch + @echo -n "Unapplying patches..."; \ + if $(QUILT) applied >/dev/null 2>/dev/null; then \ + if $(QUILT) pop -a -v >$(STAMP_DIR)/log/unpatch 2>&1; then \ + cat $(STAMP_DIR)/log/unpatch; \ + echo "successful."; \ + else \ + cat $(STAMP_DIR)/log/unpatch; \ + echo "failed! (check $(STAMP_DIR)/log/unpatch for details)"; \ + exit 1; \ + fi; \ + else \ + echo "nothing to do."; \ + fi + +# Clean the generated maintainer scripts. +.PHONY: cleanscripts +cleanscripts: + rm -f $(STAMP_DIR)/genscripts + rm -f debian/*.config \ + debian/*.postinst \ + debian/*.postrm \ + debian/*.preinst \ + debian/*.prerm + +# Clean the package build tree. +.PHONY: xsfclean +xsfclean: cleanscripts unpatch + dh_testdir + rm -rf .pc + rm -rf $(STAMP_DIR) $(SOURCE_DIR) + rm -rf imports + dh_clean debian/shlibs.local \ + debian/po/pothead + +# Generate the debconf templates POT file header. +debian/po/pothead: debian/po/pothead.in + sed -e 's/SOURCE_VERSION/$(SOURCE_VERSION)/' \ + -e 's/DATE/$(shell date "+%F %X%z"/)' <$< >$@ + +# Update POT and PO files. +.PHONY: updatepo +updatepo: debian/po/pothead + debian/scripts/debconf-updatepo --pot-header=pothead --verbose + +# Remove files from the upstream source tree that we don't need, or which have +# licensing problems. It must be run before creating the .orig.tar.gz. +# +# Note: This rule is for Debian package maintainers' convenience, and is not +# needed for conventional build scenarios. +.PHONY: prune-upstream-tree +prune-upstream-tree: + # Ensure we're in the correct directory. + dh_testdir + grep -rvh '^#' debian/prune/ | xargs --no-run-if-empty rm -rf + +# Verify that there are no offsets or fuzz in the patches we apply. +# +# Note: This rule is for Debian package maintainers' convenience, and is not +# needed for conventional build scenarios. +.PHONY: patch-audit +patch-audit: prepare unpatch + @echo -n "Auditing patches..."; \ + >$(STAMP_DIR)/log/patch; \ + FUZZY=; \ + while [ -n "$$($(QUILT) next)" ]; do \ + RESULT=$$($(QUILT) push -v | tee -a $(STAMP_DIR)/log/patch | grep ^Hunk | sed 's/^Hunk.*\(succeeded\|FAILED\).*/\1/');\ + case "$$RESULT" in \ + succeeded) \ + echo "fuzzy patch: $$($(QUILT) top)" \ + | tee -a $(STAMP_DIR)/log/$$($(QUILT) top); \ + FUZZY=yes; \ + ;; \ + FAILED) \ + echo "broken patch: $$($(QUILT) next)" \ + | tee -a $(STAMP_DIR)/log/$$($(QUILT) next); \ + exit 1; \ + ;; \ + esac; \ + done; \ + if [ -n "$$FUZZY" ]; then \ + echo "there were fuzzy patches; please fix."; \ + exit 1; \ + else \ + echo "done."; \ + fi + +# Generate the maintainer scripts. +.PHONY: genscripts +stampdir_targets+=genscripts +genscripts: $(STAMP_DIR)/genscripts +$(STAMP_DIR)/genscripts: $(STAMP_DIR)/stampdir + for FILE in debian/*.config.in \ + debian/*.postinst.in \ + debian/*.postrm.in \ + debian/*.preinst.in \ + debian/*.prerm.in; do \ + if [ -e "$$FILE" ]; then \ + MAINTSCRIPT=$$(echo $$FILE | sed 's/.in$$//'); \ + sed -n '1,/^#INCLUDE_SHELL_LIB#$$/p' <$$FILE \ + | sed -e '/^#INCLUDE_SHELL_LIB#$$/d' >$$MAINTSCRIPT.tmp; \ + cat debian/xsfbs/xsfbs.sh >>$$MAINTSCRIPT.tmp; \ + sed -n '/^#INCLUDE_SHELL_LIB#$$/,$$p' <$$FILE \ + | sed -e '/^#INCLUDE_SHELL_LIB#$$/d' >>$$MAINTSCRIPT.tmp; \ + sed -e 's/@SOURCE_VERSION@/$(SOURCE_VERSION)/' \ + -e 's/@OFFICIAL_BUILD@/$(OFFICIAL_BUILD)/' \ + -e 's/@DEFAULT_DCRESOLUTIONS@/$(DEFAULT_DCRESOLUTIONS)/' \ + <$$MAINTSCRIPT.tmp >$$MAINTSCRIPT; \ + rm $$MAINTSCRIPT.tmp; \ + fi; \ + done + # Validate syntax of generated shell scripts. + #sh debian/scripts/validate-posix-sh debian/*.config \ + # debian/*.postinst \ + # debian/*.postrm \ + # debian/*.preinst \ + # debian/*.prerm + >$@ + +# Generate the shlibs.local file. +debian/shlibs.local: + cat debian/*.shlibs >$@ + +SERVERMINVERS = $(shell cat /usr/share/xserver-xorg/serverminver 2>/dev/null) +VIDEOABI = $(shell cat /usr/share/xserver-xorg/videoabiver 2>/dev/null) +INPUTABI = $(shell cat /usr/share/xserver-xorg/inputabiver 2>/dev/null) +SERVER_DEPENDS = xserver-xorg-core (>= $(SERVERMINVERS)) +VIDDRIVER_PROVIDES = xserver-xorg-video-$(VIDEOABI) +INPDRIVER_PROVIDES = xserver-xorg-input-$(INPUTABI) +ifeq ($(PACKAGE),) +PACKAGE=$(shell awk '/^Package:/ { print $$2; exit }' < debian/control) +endif + +.PHONY: serverabi +serverabi: install +ifeq ($(SERVERMINVERS),) + @echo error: xserver-xorg-dev needs to be installed + @exit 1 +else + echo "xserver:Depends=$(SERVER_DEPENDS)" >> debian/$(PACKAGE).substvars + echo "xviddriver:Provides=$(VIDDRIVER_PROVIDES)" >> debian/$(PACKAGE).substvars + echo "xinpdriver:Provides=$(INPDRIVER_PROVIDES)" >> debian/$(PACKAGE).substvars +endif + +# vim:set noet ai sts=8 sw=8 tw=0: diff --git a/debian/xsfbs/xsfbs.sh b/debian/xsfbs/xsfbs.sh new file mode 100644 index 0000000..781826f --- /dev/null +++ b/debian/xsfbs/xsfbs.sh @@ -0,0 +1,853 @@ +# $Id$ + +# This is the X Strike Force shell library for X Window System package +# maintainer scripts. It serves to define shell functions commonly used by +# such packages, and performs some error checking necessary for proper operation +# of those functions. By itself, it does not "do" much; the maintainer scripts +# invoke the functions defined here to accomplish package installation and +# removal tasks. + +# If you are reading this within a Debian package maintainer script (e.g., +# /var/lib/dpkg)info/PACKAGE.{config,preinst,postinst,prerm,postrm}), you can +# skip past this library by scanning forward in this file to the string +# "GOBSTOPPER". + +SOURCE_VERSION=@SOURCE_VERSION@ +OFFICIAL_BUILD=@OFFICIAL_BUILD@ + +# Use special abnormal exit codes so that problems with this library are more +# easily tracked down. +SHELL_LIB_INTERNAL_ERROR=86 +SHELL_LIB_THROWN_ERROR=74 +SHELL_LIB_USAGE_ERROR=99 + +# old -> new variable names +if [ -z "$DEBUG_XORG_PACKAGE" ] && [ -n "$DEBUG_XFREE86_PACKAGE" ]; then + DEBUG_XORG_PACKAGE="$DEBUG_XFREE86_PACKAGE" +fi +if [ -z "$DEBUG_XORG_DEBCONF" ] && [ -n "$DEBUG_XFREE86_DEBCONF" ]; then + DEBUG_XORG_DEBCONF="$DEBUG_XFREE86_DEBCONF" +fi + +# initial sanity checks +if [ -z "$THIS_PACKAGE" ]; then + cat >&2 <<EOF +Error: package maintainer script attempted to use shell library without +definining \$THIS_PACKAGE shell variable. Please report the package name, +version, and the text of this error message to the Debian Bug Tracking System. +Visit <http://www.debian.org/Bugs/Reporting> on the World Wide Web for +instructions, read the file /usr/share/doc/debian/bug-reporting.txt from the +"doc-debian" package, or install the "reportbug" package and use the command of +the same name to file a report against version $SOURCE_VERSION of this package. +EOF + exit $SHELL_LIB_USAGE_ERROR +fi + +if [ -z "$THIS_SCRIPT" ]; then + cat >&2 <<EOF +Error: package maintainer script attempted to use shell library without +definining \$THIS_SCRIPT shell variable. Please report the package name, +version, and the text of this error message to the Debian Bug Tracking System. +Visit <http://www.debian.org/Bugs/Reporting> on the World Wide Web for +instructions, read the file /usr/share/doc/debian/bug-reporting.txt from the +"doc-debian" package, or install the "reportbug" package and use the command of +the same name to file a report against version $SOURCE_VERSION of the +"$THIS_PACKAGE" package. +EOF + exit $SHELL_LIB_USAGE_ERROR +fi + +if [ "$1" = "reconfigure" ] || [ -n "$DEBCONF_RECONFIGURE" ]; then + RECONFIGURE="true" +else + RECONFIGURE= +fi + +if ([ "$1" = "install" ] || [ "$1" = "configure" ]) && [ -z "$2" ]; then + FIRSTINST="yes" +fi + +if [ -z "$RECONFIGURE" ] && [ -z "$FIRSTINST" ]; then + UPGRADE="yes" +fi + +trap "message;\ + message \"Received signal. Aborting $THIS_PACKAGE package $THIS_SCRIPT script.\";\ + message;\ + exit 1" HUP INT QUIT TERM + +reject_nondigits () { + # syntax: reject_nondigits [ operand ... ] + # + # scan operands (typically shell variables whose values cannot be trusted) for + # characters other than decimal digits and barf if any are found + while [ -n "$1" ]; do + # does the operand contain anything but digits? + if ! expr "$1" : "[[:digit:]]\+$" > /dev/null 2>&1; then + # can't use die(), because it wraps message() which wraps this function + echo "$THIS_PACKAGE $THIS_SCRIPT error: reject_nondigits() encountered" \ + "possibly malicious garbage \"$1\"" >&2 + exit $SHELL_LIB_THROWN_ERROR + fi + shift + done +} + +reject_whitespace () { + # syntax: reject_whitespace [ operand ] + # + # scan operand (typically a shell variable whose value cannot be trusted) for + # whitespace characters and barf if any are found + if [ -n "$1" ]; then + # does the operand contain any whitespace? + if expr "$1" : "[[:space:]]" > /dev/null 2>&1; then + # can't use die(), because I want to avoid forward references + echo "$THIS_PACKAGE $THIS_SCRIPT error: reject_whitespace() encountered" \ + "possibly malicious garbage \"$1\"" >&2 + exit $SHELL_LIB_THROWN_ERROR + fi + fi +} + +reject_unlikely_path_chars () { + # syntax: reject_unlikely_path_chars [ operand ... ] + # + # scan operands (typically shell variables whose values cannot be trusted) for + # characters unlikely to be seen in a path and which the shell might + # interpret and barf if any are found + while [ -n "$1" ]; do + # does the operand contain any funny characters? + if expr "$1" : '.*[!$&()*;<>?|].*' > /dev/null 2>&1; then + # can't use die(), because I want to avoid forward references + echo "$THIS_PACKAGE $THIS_SCRIPT error: reject_unlikely_path_chars()" \ + "encountered possibly malicious garbage \"$1\"" >&2 + exit $SHELL_LIB_THROWN_ERROR + fi + shift + done +} + +# Query the terminal to establish a default number of columns to use for +# displaying messages to the user. This is used only as a fallback in the +# event the COLUMNS variable is not set. ($COLUMNS can react to SIGWINCH while +# the script is running, and this cannot, only being calculated once.) +DEFCOLUMNS=$(stty size 2> /dev/null | awk '{print $2}') || true +if ! expr "$DEFCOLUMNS" : "[[:digit:]]\+$" > /dev/null 2>&1; then + DEFCOLUMNS=80 +fi + +message () { + # pretty-print messages of arbitrary length + reject_nondigits "$COLUMNS" + echo "$*" | fmt -t -w ${COLUMNS:-$DEFCOLUMNS} >&2 +} + +observe () { + # syntax: observe message ... + # + # issue observational message suitable for logging someday when support for + # it exists in dpkg + if [ -n "$DEBUG_XORG_PACKAGE" ]; then + message "$THIS_PACKAGE $THIS_SCRIPT note: $*" + fi +} + +warn () { + # syntax: warn message ... + # + # issue warning message suitable for logging someday when support for + # it exists in dpkg; also send to standard error + message "$THIS_PACKAGE $THIS_SCRIPT warning: $*" +} + +die () { + # syntax: die message ... + # + # exit script with error message + message "$THIS_PACKAGE $THIS_SCRIPT error: $*" + exit $SHELL_LIB_THROWN_ERROR +} + +internal_error () { + # exit script with error; essentially a "THIS SHOULD NEVER HAPPEN" message + message "internal error: $*" + if [ -n "$OFFICIAL_BUILD" ]; then + message "Please report a bug in the $THIS_SCRIPT script of the" \ + "$THIS_PACKAGE package, version $SOURCE_VERSION to the Debian Bug" \ + "Tracking System. Include all messages above that mention the" \ + "$THIS_PACKAGE package. Visit " \ + "<http://www.debian.org/Bugs/Reporting> on the World Wide Web for" \ + "instructions, read the file" \ + "/usr/share/doc/debian/bug-reporting.txt from the doc-debian" \ + "package, or install the reportbug package and use the command of" \ + "the same name to file a report." + fi + exit $SHELL_LIB_INTERNAL_ERROR +} + +usage_error () { + message "usage error: $*" + message "Please report a bug in the $THIS_SCRIPT script of the" \ + "$THIS_PACKAGE package, version $SOURCE_VERSION to the Debian Bug" \ + "Tracking System. Include all messages above that mention the" \ + "$THIS_PACKAGE package. Visit " \ + "<http://www.debian.org/Bugs/Reporting> on the World Wide Web for" \ + "instructions, read the file" \ + "/usr/share/doc/debian/bug-reporting.txt from the doc-debian" \ + "package, or install the reportbug package and use the command of" \ + "the same name to file a report." + exit $SHELL_LIB_USAGE_ERROR +} + + +maplink () { + # returns what symlink should point to; i.e., what the "sane" answer is + # Keep this in sync with the debian/*.links files. + # This is only needed for symlinks to directories. + # + # XXX: Most of these look wrong in the X11R7 world and need to be fixed. + # If we've stopped using this function, fixing it might enable us to re-enable + # it again and catch more errors. + case "$1" in + /etc/X11/xkb/compiled) echo /var/lib/xkb ;; + /etc/X11/xkb/xkbcomp) echo /usr/X11R6/bin/xkbcomp ;; + /usr/X11R6/lib/X11/app-defaults) echo /etc/X11/app-defaults ;; + /usr/X11R6/lib/X11/fs) echo /etc/X11/fs ;; + /usr/X11R6/lib/X11/lbxproxy) echo /etc/X11/lbxproxy ;; + /usr/X11R6/lib/X11/proxymngr) echo /etc/X11/proxymngr ;; + /usr/X11R6/lib/X11/rstart) echo /etc/X11/rstart ;; + /usr/X11R6/lib/X11/twm) echo /etc/X11/twm ;; + /usr/X11R6/lib/X11/xdm) echo /etc/X11/xdm ;; + /usr/X11R6/lib/X11/xinit) echo /etc/X11/xinit ;; + /usr/X11R6/lib/X11/xkb) echo /etc/X11/xkb ;; + /usr/X11R6/lib/X11/xserver) echo /etc/X11/xserver ;; + /usr/X11R6/lib/X11/xsm) echo /etc/X11/xsm ;; + /usr/bin/X11) echo ../X11R6/bin ;; + /usr/bin/rstartd) echo ../X11R6/bin/rstartd ;; + /usr/include/X11) echo ../X11R6/include/X11 ;; + /usr/lib/X11) echo ../X11R6/lib/X11 ;; + *) internal_error "maplink() called with unknown path \"$1\"" ;; + esac +} + +analyze_path () { + # given a supplied set of pathnames, break each one up by directory and do an + # ls -dl on each component, cumulatively; i.e. + # analyze_path /usr/X11R6/bin -> ls -dl /usr /usr/X11R6 /usr/X11R6/bin + # Thanks to Randolph Chung for this clever hack. + + local f g + + while [ -n "$1" ]; do + reject_whitespace "$1" + g= + message "Analyzing $1:" + for f in $(echo "$1" | tr / \ ); do + if [ -e /$g$f ]; then + ls -dl /$g$f /$g$f.dpkg-* 2> /dev/null || true + g=$g$f/ + else + message "/$g$f: nonexistent; directory contents of /$g:" + ls -l /$g + break + fi + done + shift + done +} + +find_culprits () { + local f p dpkg_info_dir possible_culprits smoking_guns bad_packages package \ + msg + + reject_whitespace "$1" + message "Searching for overlapping packages..." + dpkg_info_dir=/var/lib/dpkg/info + if [ -d $dpkg_info_dir ]; then + if [ "$(echo $dpkg_info_dir/*.list)" != "$dpkg_info_dir/*.list" ]; then + possible_culprits=$(ls -1 $dpkg_info_dir/*.list | egrep -v \ + "(xbase-clients|x11-common|xfs|xlibs)") + if [ -n "$possible_culprits" ]; then + smoking_guns=$(grep -l "$1" $possible_culprits || true) + if [ -n "$smoking_guns" ]; then + bad_packages=$(printf "\\n") + for f in $smoking_guns; do + # too bad you can't nest parameter expansion voodoo + p=${f%*.list} # strip off the trailing ".list" + package=${p##*/} # strip off the directories + bad_packages=$(printf "%s\n%s" "$bad_packages" "$package") + done + msg=$(cat <<EOF +The following packages appear to have file overlaps with the X.Org packages; +these packages are either very old, or in violation of Debian Policy. Try +upgrading each of these packages to the latest available version if possible: +for example, with the command "apt-get install". If no newer version of a +package is available, you will have to remove it; for example, with the command +"apt-get remove". If even the latest available version of the package has +this file overlap, please file a bug against that package with the Debian Bug +Tracking System. You may want to refer the package maintainer to section 12.8 +of the Debian Policy manual. +EOF +) + message "$msg" + message "The overlapping packages are: $bad_packages" + else + message "no overlaps found." + fi + fi + else + message "cannot search; no matches for $dpkg_info_dir/*.list." + fi + else + message "cannot search; $dpkg_info_dir does not exist." + fi +} + +check_symlink () { + # syntax: check_symlink symlink + # + # See if specified symlink points where it is supposed to. Return 0 if it + # does, and 1 if it does not. + # + # Primarily used by check_symlinks_and_warn() and check_symlinks_and_bomb(). + + local symlink + + # validate arguments + if [ $# -ne 1 ]; then + usage_error "check_symlink() called with wrong number of arguments;" \ + "expected 1, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + symlink="$1" + + if [ "$(maplink "$symlink")" = "$(readlink "$symlink")" ]; then + return 0 + else + return 1 + fi +} + +check_symlinks_and_warn () { + # syntax: check_symlinks_and_warn symlink ... + # + # For each argument, check for symlink sanity, and warn if it isn't sane. + # + # Call this function from a preinst script in the event $1 is "upgrade" or + # "install". + + local errmsg symlink + + # validate arguments + if [ $# -lt 1 ]; then + usage_error "check_symlinks_and_warn() called with wrong number of" \ + "arguments; expected at least 1, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + while [ -n "$1" ]; do + symlink="$1" + if [ -L "$symlink" ]; then + if ! check_symlink "$symlink"; then + observe "$symlink symbolic link points to wrong location" \ + "$(readlink "$symlink"); removing" + rm "$symlink" + fi + elif [ -e "$symlink" ]; then + errmsg="$symlink exists and is not a symbolic link; this package cannot" + errmsg="$errmsg be installed until this" + if [ -f "$symlink" ]; then + errmsg="$errmsg file" + elif [ -d "$symlink" ]; then + errmsg="$errmsg directory" + else + errmsg="$errmsg thing" + fi + errmsg="$errmsg is removed" + die "$errmsg" + fi + shift + done +} + +check_symlinks_and_bomb () { + # syntax: check_symlinks_and_bomb symlink ... + # + # For each argument, check for symlink sanity, and bomb if it isn't sane. + # + # Call this function from a postinst script. + + local problem symlink + + # validate arguments + if [ $# -lt 1 ]; then + usage_error "check_symlinks_and_bomb() called with wrong number of" + "arguments; expected at least 1, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + while [ -n "$1" ]; do + problem= + symlink="$1" + if [ -L "$symlink" ]; then + if ! check_symlink "$symlink"; then + problem=yes + warn "$symlink symbolic link points to wrong location" \ + "$(readlink "$symlink")" + fi + elif [ -e "$symlink" ]; then + problem=yes + warn "$symlink is not a symbolic link" + else + problem=yes + warn "$symlink symbolic link does not exist" + fi + if [ -n "$problem" ]; then + analyze_path "$symlink" "$(readlink "$symlink")" + find_culprits "$symlink" + die "bad symbolic links on system" + fi + shift + done +} + +font_update () { + # run $UPDATECMDS in $FONTDIRS + + local dir cmd shortcmd x_font_dir_prefix + + x_font_dir_prefix="/usr/share/fonts/X11" + + if [ -z "$UPDATECMDS" ]; then + usage_error "font_update() called but \$UPDATECMDS not set" + fi + if [ -z "$FONTDIRS" ]; then + usage_error "font_update() called but \$FONTDIRS not set" + fi + + reject_unlikely_path_chars "$UPDATECMDS" + reject_unlikely_path_chars "$FONTDIRS" + + for dir in $FONTDIRS; do + if [ -d "$x_font_dir_prefix/$dir" ]; then + for cmd in $UPDATECMDS; do + if which "$cmd" > /dev/null 2>&1; then + shortcmd=${cmd##*/} + observe "running $shortcmd in $dir font directory" + cmd_opts= + if [ "$shortcmd" = "update-fonts-alias" ]; then + cmd_opts=--x11r7-layout + fi + if [ "$shortcmd" = "update-fonts-dir" ]; then + cmd_opts=--x11r7-layout + fi + if [ "$shortcmd" = "update-fonts-scale" ]; then + cmd_opts=--x11r7-layout + fi + $cmd $cmd_opts $dir || warn "$cmd $cmd_opts $dir" \ + "failed; font directory data may not" \ + "be up to date" + else + warn "$cmd not found; not updating corresponding $dir font" \ + "directory data" + fi + done + else + warn "$dir is not a directory; not updating font directory data" + fi + done +} + +remove_conffile_prepare () { + # syntax: remove_conffile_prepare filename official_md5sum ... + # + # Check a conffile "filename" against a list of canonical MD5 checksums. + # If the file's current MD5 checksum matches one of the "official_md5sum" + # operands provided, then prepare the conffile for removal from the system. + # We defer actual deletion until the package is configured so that we can + # roll this operation back if package installation fails. + # + # Call this function from a preinst script in the event $1 is "upgrade" or + # "install" and verify $2 to ensure the package is being upgraded from a + # version (or installed over a version removed-but-not-purged) prior to the + # one in which the conffile was obsoleted. + + local conffile current_checksum + + # validate arguments + if [ $# -lt 2 ]; then + usage_error "remove_conffile_prepare() called with wrong number of" \ + "arguments; expected at least 2, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + conffile="$1" + shift + + # does the conffile even exist? + if [ -e "$conffile" ]; then + # calculate its checksum + current_checksum=$(md5sum < "$conffile" | sed 's/[[:space:]].*//') + # compare it to each supplied checksum + while [ -n "$1" ]; do + if [ "$current_checksum" = "$1" ]; then + # we found a match; move the confffile and stop looking + observe "preparing obsolete conffile $conffile for removal" + mv "$conffile" "$conffile.$THIS_PACKAGE-tmp" + break + fi + shift + done + fi +} + +remove_conffile_lookup () { + # syntax: remove_conffile_lookup package filename + # + # Lookup the md5sum of a conffile in dpkg's database, and prepare for removal + # if it matches the actual file's md5sum. + # + # Call this function when you would call remove_conffile_prepare but only + # want to check against dpkg's status database instead of known checksums. + + local package conffile old_md5sum + + # validate arguments + if [ $# -ne 2 ]; then + usage_error "remove_conffile_lookup() called with wrong number of" \ + "arguments; expected 1, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + package="$1" + conffile="$2" + + if ! [ -e "$conffile" ]; then + return + fi + old_md5sum="$(dpkg-query -W -f='${Conffiles}' "$package" | \ + awk '{ if (match($0, "^ '"$conffile"' ")) print $2}')" + if [ -n "$old_md5sum" ]; then + remove_conffile_prepare "$conffile" "$old_md5sum" + fi +} + +remove_conffile_commit () { + # syntax: remove_conffile_commit filename + # + # Complete the removal of a conffile "filename" that has become obsolete. + # + # Call this function from a postinst script after having used + # remove_conffile_prepare() in the preinst. + + local conffile + + # validate arguments + if [ $# -ne 1 ]; then + usage_error "remove_conffile_commit() called with wrong number of" \ + "arguments; expected 1, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + conffile="$1" + + # if the temporary file created by remove_conffile_prepare() exists, remove it + if [ -e "$conffile.$THIS_PACKAGE-tmp" ]; then + observe "committing removal of obsolete conffile $conffile" + rm "$conffile.$THIS_PACKAGE-tmp" + fi +} + +remove_conffile_rollback () { + # syntax: remove_conffile_rollback filename + # + # Roll back the removal of a conffile "filename". + # + # Call this function from a postrm script in the event $1 is "abort-upgrade" + # or "abort-install" is after having used remove_conffile_prepare() in the + # preinst. + + local conffile + + # validate arguments + if [ $# -ne 1 ]; then + usage_error "remove_conffile_rollback() called with wrong number of" \ + "arguments; expected 1, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + conffile="$1" + + # if the temporary file created by remove_conffile_prepare() exists, move it + # back + if [ -e "$conffile.$THIS_PACKAGE-tmp" ]; then + observe "rolling back removal of obsolete conffile $conffile" + mv "$conffile.$THIS_PACKAGE-tmp" "$conffile" + fi +} + +replace_conffile_with_symlink_prepare () { + # syntax: replace_conffile_with_symlink_prepare oldfilename newfilename \ + # official_md5sum ... + # + # Check a conffile "oldfilename" against a list of canonical MD5 checksums. + # If the file's current MD5 checksum matches one of the "official_md5sum" + # operands provided, then prepare the conffile for removal from the system. + # We defer actual deletion until the package is configured so that we can + # roll this operation back if package installation fails. Otherwise copy it + # to newfilename and let dpkg handle it through conffiles mechanism. + # + # Call this function from a preinst script in the event $1 is "upgrade" or + # "install" and verify $2 to ensure the package is being upgraded from a + # version (or installed over a version removed-but-not-purged) prior to the + # one in which the conffile was obsoleted. + + local conffile current_checksum + + # validate arguments + if [ $# -lt 3 ]; then + usage_error "replace_conffile_with_symlink_prepare() called with wrong" \ + " number of arguments; expected at least 3, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + oldconffile="$1" + shift + newconffile="$1" + shift + + remove_conffile_prepare "$_oldconffile" "$@" + # If $oldconffile still exists, then md5sums didn't match. + # Copy it to new one. + if [ -f "$oldconffile" ]; then + cp "$oldconffile" "$newconffile" + fi + +} + +replace_conffile_with_symlink_commit () { + # syntax: replace_conffile_with_symlink_commit oldfilename + # + # Complete the removal of a conffile "oldfilename" that has been + # replaced by a symlink. + # + # Call this function from a postinst script after having used + # replace_conffile_with_symlink_prepare() in the preinst. + + local conffile + + # validate arguments + if [ $# -ne 1 ]; then + usage_error "replace_conffile_with_symlink_commit() called with wrong" \ + "number of arguments; expected 1, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + conffile="$1" + + remove_conffile_commit "$conffile" +} + +replace_conffile_with_symlink_rollback () { + # syntax: replace_conffile_with_symlink_rollback oldfilename newfilename + # + # Roll back the replacing of a conffile "oldfilename" with symlink to + # "newfilename". + # + # Call this function from a postrm script in the event $1 is "abort-upgrade" + # or "abort-install" and verify $2 to ensure the package failed to upgrade + # from a version (or install over a version removed-but-not-purged) prior + # to the one in which the conffile was obsoleted. + # You should have used replace_conffile_with_symlink_prepare() in the + # preinst. + + local conffile + + # validate arguments + if [ $# -ne 2 ]; then + usage_error "replace_conffile_with_symlink_rollback() called with wrong" \ + "number of arguments; expected 2, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + oldconffile="$1" + newconffile="$2" + + remove_conffile_rollback "$_oldconffile" + if [ -f "$newconffile" ]; then + rm "$newconffile" + fi +} + +run () { + # syntax: run command [ argument ... ] + # + # Run specified command with optional arguments and report its exit status. + # Useful for commands whose exit status may be nonzero, but still acceptable, + # or commands whose failure is not fatal to us. + # + # NOTE: Do *not* use this function with db_get or db_metaget commands; in + # those cases the return value of the debconf command *must* be checked + # before the string returned by debconf is used for anything. + + local retval + + # validate arguments + if [ $# -lt 1 ]; then + usage_error "run() called with wrong number of arguments; expected at" \ + "least 1, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + "$@" || retval=$? + + if [ ${retval:-0} -ne 0 ]; then + observe "command \"$*\" exited with status $retval" + fi +} + +make_symlink_sane () { + # syntax: make_symlink_sane symlink target + # + # Ensure that the symbolic link symlink exists, and points to target. + # + # If symlink does not exist, create it and point it at target. + # + # If symlink exists but is not a symbolic link, back it up. + # + # If symlink exists, is a symbolic link, but points to the wrong location, fix + # it. + # + # If symlink exists, is a symbolic link, and already points to target, do + # nothing. + # + # This function wouldn't be needed if ln had an -I, --idempotent option. + + # Validate arguments. + if [ $# -ne 2 ]; then + usage_error "make_symlink_sane() called with wrong number of arguments;" \ + "expected 2, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + # We could just use the positional parameters as-is, but that makes things + # harder to follow. + local symlink target + + symlink="$1" + target="$2" + + if [ -L "$symlink" ] && [ "$(readlink "$symlink")" = "$target" ]; then + observe "link from $symlink to $target already exists" + else + observe "creating symbolic link from $symlink to $target" + mkdir -p "${target%/*}" "${symlink%/*}" + ln -s -b -S ".dpkg-old" "$target" "$symlink" + fi +} + +migrate_dir_to_symlink () { + # syntax: migrate_dir_to_symlink old_location new_location + # + # Per Debian Policy section 6.5.4, "A directory will never be replaced by a + # symbolic link to a directory or vice versa; instead, the existing state + # (symlink or not) will be left alone and dpkg will follow the symlink if + # there is one." + # + # We have to do it ourselves. + # + # This function moves the contents of old_location, a directory, into + # new_location, a directory, then makes old_location a symbolic link to + # new_location. + # + # old_location need not exist, but if it does, it must be a directory (or a + # symlink to a directory). If it is not, it is backed up. If new_location + # exists already and is not a directory, it is backed up. + # + # This function should be called from a package's preinst so that other + # packages unpacked after this one --- but before this package's postinst runs + # --- are unpacked into new_location even if their payloads contain + # old_location filespecs. + + # Validate arguments. + if [ $# -ne 2 ]; then + usage_error "migrate_dir_to_symlink() called with wrong number of" + "arguments; expected 2, got $#" + exit $SHELL_LIB_USAGE_ERROR + fi + + # We could just use the positional parameters as-is, but that makes things + # harder to follow. + local new old + + old="$1" + new="$2" + + # Is old location a symlink? + if [ -L "$old" ]; then + # Does it already point to new location? + if [ "$(readlink "$old")" = "$new" ]; then + # Nothing to do; migration has already been done. + observe "migration of $old to $new already done" + return 0 + else + # Back it up. + warn "backing up symbolic link $old as $old.dpkg-old" + mv -b "$old" "$old.dpkg-old" + fi + fi + + # Does old location exist, but is not a directory? + if [ -e "$old" ] && ! [ -d "$old" ]; then + # Back it up. + warn "backing up non-directory $old as $old.dpkg-old" + mv -b "$old" "$old.dpkg-old" + fi + + observe "migrating $old to $new" + + # Is new location a symlink? + if [ -L "$new" ]; then + # Does it point the wrong way, i.e., back to where we're migrating from? + if [ "$(readlink "$new")" = "$old" ]; then + # Get rid of it. + observe "removing symbolic link $new which points to $old" + rm "$new" + else + # Back it up. + warn "backing up symbolic link $new as $new.dpkg-old" + mv -b "$new" "$new.dpkg-old" + fi + fi + + # Does new location exist, but is not a directory? + if [ -e "$new" ] && ! [ -d "$new" ]; then + warn "backing up non-directory $new as $new.dpkg-old" + mv -b "$new" "$new.dpkg-old" + fi + + # Create new directory if it does not yet exist. + if ! [ -e "$new" ]; then + observe "creating $new" + mkdir -p "$new" + fi + + # Copy files in old location to new location. Back up any filenames that + # already exist in the new location with the extension ".dpkg-old". + observe "copying files from $old to $new" + if ! (cd "$old" && cp -a -b -S ".dpkg-old" . "$new"); then + die "error(s) encountered while copying files from $old to $new" + fi + + # Remove files at old location. + observe "removing $old" + rm -r "$old" + + # Create symlink from old location to new location. + make_symlink_sane "$old" "$new" +} + +# vim:set ai et sw=2 ts=2 tw=80: + +# GOBSTOPPER: The X Strike Force shell library ends here. diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..58d7c39 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = evdev-properties.h +sdk_HEADERS = evdev-properties.h diff --git a/include/evdev-properties.h b/include/evdev-properties.h new file mode 100755 index 0000000..3e3c194 --- /dev/null +++ b/include/evdev-properties.h @@ -0,0 +1,69 @@ +/* + * Copyright © 2008 Red Hat, Inc. + * + * 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 Red Hat + * not be used in advertising or publicity pertaining to distribution + * of the software without specific, written prior permission. Red + * Hat makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS 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. + * + * Authors: + * Peter Hutterer (peter.hutterer@redhat.com) + */ + + +#ifndef _EVDEV_PROPERTIES_H_ +#define _EVDEV_PROPERTIES_H_ + +/* Middle mouse button emulation */ +/* BOOL */ +#define EVDEV_PROP_MIDBUTTON "Evdev Middle Button Emulation" +/* CARD32 */ +#define EVDEV_PROP_MIDBUTTON_TIMEOUT "Evdev Middle Button Timeout" + +/* Wheel emulation */ +/* BOOL */ +#define EVDEV_PROP_WHEEL "Evdev Wheel Emulation" +/* CARD8, 4 values [x up, x down, y up, y down], 0 to disable a value*/ +#define EVDEV_PROP_WHEEL_AXES "Evdev Wheel Emulation Axes" +/* CARD16 */ +#define EVDEV_PROP_WHEEL_INERTIA "Evdev Wheel Emulation Inertia" +/* CARD16 */ +#define EVDEV_PROP_WHEEL_TIMEOUT "Evdev Wheel Emulation Timeout" +/* CARD8, value range 0-32, 0 to always scroll */ +#define EVDEV_PROP_WHEEL_BUTTON "Evdev Wheel Emulation Button" + +/* Drag lock */ +/* CARD8, either 1 value or pairs, value range 0-32, 0 to disable a value*/ +#define EVDEV_PROP_DRAGLOCK "Evdev Drag Lock Buttons" + +/* Axis inversion */ +/* BOOL, 2 values [x, y], 1 inverts axis */ +#define EVDEV_PROP_INVERT_AXES "Evdev Axis Inversion" + +/* Reopen attempts. */ +/* CARD8 */ +#define EVDEV_PROP_REOPEN "Evdev Reopen Attempts" + +/* Run-time calibration */ +/* CARD32, 4 values [minx, maxx, miny, maxy], or no values for unset */ +#define EVDEV_PROP_CALIBRATION "Evdev Axis Calibration" + +/* Swap x and y axis. */ +/* BOOL */ +#define EVDEV_PROP_SWAP_AXES "Evdev Axes Swap" + +#endif diff --git a/man/Makefile.am b/man/Makefile.am new file mode 100644 index 0000000..276f2f0 --- /dev/null +++ b/man/Makefile.am @@ -0,0 +1,57 @@ +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# +# 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. +# +# 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 OPEN GROUP 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. +# +# Except as contained in this notice, the name of the copyright holders 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 holders. +# + +drivermandir = $(DRIVER_MAN_DIR) + +driverman_PRE = @DRIVER_NAME@.man + +driverman_DATA = $(driverman_PRE:man=@DRIVER_MAN_SUFFIX@) + +EXTRA_DIST = @DRIVER_NAME@.man + +CLEANFILES = $(driverman_DATA) + +SED = sed + +# Strings to replace in man pages +XORGRELSTRING = @PACKAGE_STRING@ + XORGMANNAME = X Version 11 + +MAN_SUBSTS = \ + -e 's|__vendorversion__|"$(XORGRELSTRING)" "$(XORGMANNAME)"|' \ + -e 's|__xorgversion__|"$(XORGRELSTRING)" "$(XORGMANNAME)"|' \ + -e 's|__xservername__|Xorg|g' \ + -e 's|__xconfigfile__|xorg.conf|g' \ + -e 's|__projectroot__|$(prefix)|g' \ + -e 's|__appmansuffix__|$(APP_MAN_SUFFIX)|g' \ + -e 's|__drivermansuffix__|$(DRIVER_MAN_SUFFIX)|g' \ + -e 's|__adminmansuffix__|$(ADMIN_MAN_SUFFIX)|g' \ + -e 's|__miscmansuffix__|$(MISC_MAN_SUFFIX)|g' \ + -e 's|__filemansuffix__|$(FILE_MAN_SUFFIX)|g' + +SUFFIXES = .$(DRIVER_MAN_SUFFIX) .man + +.man.$(DRIVER_MAN_SUFFIX): + sed $(MAN_SUBSTS) < $< > $@ diff --git a/man/evdev.man b/man/evdev.man new file mode 100644 index 0000000..9dd7a77 --- /dev/null +++ b/man/evdev.man @@ -0,0 +1,231 @@ +.\" shorthand for double quote that works everywhere. +.ds q \N'34' +.TH EVDEV __drivermansuffix__ __vendorversion__ +.SH NAME +evdev \- Generic Linux input driver +.SH SYNOPSIS +.nf +.B "Section \*qInputDevice\*q" +.BI " Identifier \*q" devname \*q +.B " Driver \*qevdev\*q" +.BI " Option \*qDevice\*q \*q" devpath \*q +.BI " Option \*qEmulate3Buttons\*q \*q" True \*q +.BI " Option \*qEmulate3Timeout\*q \*q" 50 \*q +.BI " Option \*qGrabDevice\*q \*q" False \*q +\ \ ... +.B EndSection +.fi +.SH DESCRIPTION +.B evdev +is an __xservername__ input driver for Linux\'s generic event devices. It +therefore supports all input devices that the kernel knows about, including +most mice and keyboards. +.PP +The +.B evdev +driver can serve as both a pointer and a keyboard input device, and may be +used as both the core keyboard and the core pointer. Multiple input devices +are supported by multiple instances of this driver, with one Load +directive for evdev in the Module section of your __xconfigfile__ for each +input device that will use this driver. +.PP +.SH SUPPORTED HARDWARE +In general, any input device that the kernel has a driver for can be accessed +through the +.B evdev +driver. See the Linux kernel documentation for a complete list. +.PP +.SH CONFIGURATION DETAILS +Please refer to __xconfigfile__(__filemansuffix__) for general configuration +details and for options that can be used with all input drivers. This +section only covers configuration details specific to this driver. +.PP +The following driver +.B Options +are supported: +.TP 7 +.BI "Option \*qButtonMapping\*q \*q" string \*q +Sets the button mapping for this device. The mapping is a space-separated list +of button mappings that correspond in order to the physical buttons on the +device (i.e. the first number is the mapping for button 1, etc.). The default +mapping is "1 2 3 ... 32". A mapping of 0 deactivates the button. Multiple +buttons can have the same mapping. +For example, a left-handed mouse with deactivated scroll-wheel would use a +mapping of "3 2 1 0 0". Invalid mappings are ignored and the default mapping +is used. Buttons not specified in the user's mapping use the default mapping. +.TP 7 +.BI "Option \*qDevice\*q \*q" string \*q +Specifies the device through which the device can be accessed. This will +generally be of the form \*q/dev/input/eventX\*q, where X is some integer. +The mapping from device node to hardware is system-dependent. +.TP 7 +.BI "Option \*qDragLockButtons\*q \*q" "L1 B2 L3 B4" \*q +Sets \*qdrag lock buttons\*q that simulate holding a button down, so +that low dexterity people do not have to hold a button down at the +same time they move a mouse cursor. Button numbers occur in pairs, +with the lock button number occurring first, followed by the button +number that is the target of the lock button. Property: "Evdev +Drag Lock Buttons". +.TP 7 +.BI "Option \*qDragLockButtons\*q \*q" "M1" \*q +Sets a \*qmaster drag lock button\*q that acts as a \*qMeta Key\*q +indicating that the next button pressed is to be +\*qdrag locked\*q. Property: "Evdev Drag Lock Buttons". +.TP 7 +.TP 7 +.BI "Option \*qEmulate3Buttons\*q \*q" boolean \*q +Enable/disable the emulation of the third (middle) mouse button for mice +which only have two physical buttons. The third button is emulated by +pressing both buttons simultaneously. Default: on, until a middle mouse +button event is registered. Property: "Evdev Middle Button Emulation". +.TP 7 +.BI "Option \*qEmulate3Timeout\*q \*q" integer \*q +Sets the timeout (in milliseconds) that the driver waits before deciding +if two buttons where pressed "simultaneously" when 3 button emulation is +enabled. Default: 50. Property: "Evdev Middle Button Timeout". +.BI "Option \*qEmulateWheel\*q \*q" boolean \*q +Enable/disable "wheel" emulation. Wheel emulation means emulating button +press/release events when the mouse is moved while a specific real button +is pressed. Wheel button events (typically buttons 4 and 5) are +usually used for scrolling. Wheel emulation is useful for getting wheel-like +behaviour with trackballs. It can also be useful for mice with 4 or +more buttons but no wheel. See the description of the +.BR EmulateWheelButton , +.BR EmulateWheelInertia , +.BR EmulateWheelTimeout , +.BR XAxisMapping , +and +.B YAxisMapping +options. Default: off. Property "Evdev Wheel Emulation". +.TP 7 +.BI "Option \*qEmulateWheelButton\*q \*q" integer \*q +Specifies which button must be held down to enable wheel emulation mode. +While this button is down, X and/or Y pointer movement will generate button +press/release events as specified for the +.B XAxisMapping +and +.B YAxisMapping +settings. If the button is 0 and +.BR EmulateWheel +is on, any motion of the device is converted into wheel events. Default: 4. +Property: "Evdev Wheel Emulation Button". +.TP 7 +.BI "Option \*qEmulateWheelInertia\*q \*q" integer \*q +Specifies how far (in pixels) the pointer must move to generate button +press/release events in wheel emulation mode. Default: 10. Property: "Evdev +Wheel Emulation Inertia". +.TP 7 +.BI "Option \*qEmulateWheelTimeout\*q \*q" integer \*q +Specifies the time in milliseconds the +.BR EmulateWheelButton +must be pressed before wheel emulation is started. If the +.BR EmulateWheelButton +is released before this timeout, the original button press/release event +is sent. Default: 200. Property: "Evdev Wheel Emulation Timeout". +.TP 7 +.BI "Option \*qGrabDevice\*q \*q" boolean \*q +Force a grab on the event device. Doing so will ensure that no other driver +can initialise the same device and it will also stop the device from sending +events to /dev/kbd or /dev/input/mice. Events from this device will not be +sent to virtual devices (e.g. rfkill or the Macintosh mouse button emulation). +Default: disabled. +.TP 7 +.BI "Option \*qInvertX\*q \*q" Bool \*q +.TP 7 +.BI "Option \*qInvertY\*q \*q" Bool \*q +Invert the given axis. Default: off. Property: "Evdev Axis Inversion". +.TP 7 +.BI "Option \*qIgnoreRelativeAxes\*q \*q" Bool \*q +.TP 7 +.BI "Option \*qIgnoreAbsoluteAxes\*q \*q" Bool \*q +Ignore the specified type of axis. Default: unset. The X server cannot deal +with devices that have both relative and absolute axes. Evdev tries to guess +wich axes to ignore given the device type and disables absolute axes for +mice and relative axes for tablets, touchscreens and touchpad. These options +allow to forcibly disable an axis type. Mouse wheel axes are exempt and will +work even if relative axes are ignored. No property, this configuration must +be set in the configuration. +.br +If either option is set to False, the driver will not ignore the specified +axes regardless of the presence of other axes. This may trigger buggy +behavior and events from this axis are always forwarded. Users are +discouraged from setting this option. +.TP 7 +.BI "Option \*qReopenAttempts\*q \*q" integer \*q +Number of reopen attempts after a read error occurs on the device (e.g. after +waking up from suspend). In between each attempt is a 100ms wait. Default: 10. +.TP 7 +.BI "Option \*qCalibration\*q \*q" "min-x max-x min-y max-y" \*q +Calibrates the X and Y axes for devices that need to scale to a different +coordinate system than reported to the X server. This feature is required +for devices that need to scale to a different coordinate system than +originally reported by the kernel (e.g. touchscreens). The scaling to the +custom coordinate system is done in-driver and the X server is unaware of +the transformation. Property: "Evdev Axis Calibration". +.TP 7 +.BI "Option \*qSwapAxes\*q \*q" Bool \*q +Swap x/y axes. Default: off. Property: "Evdev Axes Swap". +.TP 7 +.BI "Option \*qXAxisMapping\*q \*q" "N1 N2" \*q +Specifies which buttons are mapped to motion in the X direction in wheel +emulation mode. Button number +.I N1 +is mapped to the negative X axis motion and button number +.I N2 +is mapped to the positive X axis motion. Default: no mapping. Property: +"Evdev Wheel Emulation Axes". +.TP 7 +.BI "Option \*qYAxisMapping\*q \*q" "N1 N2" \*q +Specifies which buttons are mapped to motion in the Y direction in wheel +emulation mode. Button number +.I N1 +is mapped to the negative Y axis motion and button number +.I N2 +is mapped to the positive Y axis motion. Default: "4 5". Property: +"Evdev Wheel Emulation Axes". + +.SH SUPPORTED PROPERTIES +The following properties are provided by the +.B evdev +driver. +.TP 7 +.BI "Evdev Axis Calibration" +4 32-bit values, order min-x, max-x, min-y, max-y or 0 values to disable +in-driver axis calibration. +.TP 7 +.BI "Evdev Axis Inversion" +2 boolean values (8 bit, 0 or 1), order X, Y. 1 inverts the axis. +.TP 7 +.BI "Evdev Axes Swap" +1 boolean value (8 bit, 0 or 1). 1 swaps x/y axes. +.TP 7 +.BI "Evdev Drag Lock Buttons" +8-bit. Either 1 value or pairs of values. Value range 0-32, 0 disables a +value. +.TP 7 +.BI "Evdev Middle Button Emulation" +1 boolean value (8 bit, 0 or 1). +.TP 7 +.BI "Evdev Middle Button Timeout" +1 16-bit positive value. +.TP 7 +.BI "Evdev Wheel Emulation" +1 boolean value (8 bit, 0 or 1). +.TP 7 +.BI "Evdev Wheel Emulation Axes" +4 8-bit values, order X up, X down, Y up, Y down. 0 disables a value. +.TP 7 +.BI "Evdev Wheel Emulation Button" +1 8-bit value, allowed range 0-32, 0 disables the button. +.TP 7 +.BI "Evdev Wheel Emulation Inertia" +1 16-bit positive value. +.TP 7 +.BI "Evdev Wheel Emulation Timeout" +1 16-bit positive value. + +.SH AUTHORS +Kristian Høgsberg. +.SH "SEE ALSO" +__xservername__(__appmansuffix__), __xconfigfile__(__filemansuffix__), Xserver(__appmansuffix__), X(__miscmansuffix__), +README.mouse. diff --git a/packaging/xorg-x11-drv-evdev.spec b/packaging/xorg-x11-drv-evdev.spec new file mode 100644 index 0000000..548424c --- /dev/null +++ b/packaging/xorg-x11-drv-evdev.spec @@ -0,0 +1,54 @@ +Name: xorg-x11-drv-evdev +Summary: Xorg X11 evdev input driver +Version: 2.3.2 +Release: 4 +Group: System/X Hardware Support +License: MIT +URL: http://www.x.org/ +Source0: http://xorg.freedesktop.org/releases/individual/driver/xf86-input-evdev-%{version}.tar.gz +Requires: xorg-x11-server +BuildRequires: pkgconfig(xorg-server) +BuildRequires: pkgconfig(xkbfile) +BuildRequires: pkgconfig(xproto) +BuildRequires: pkgconfig(inputproto) +BuildRequires: pkgconfig(xrandr) +BuildRequires: pkgconfig(randrproto) +BuildRequires: pkgconfig(xextproto) + + +%description +The Xorg X11 evdev input driver + + +%package devel +Summary: Development files for xorg evdev driver +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +This package contains xorg evdev development files + + +%prep +%setup -q -n xf86-input-evdev-%{version} + +%build + +%reconfigure --disable-static +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install + +%remove_docs + +%files +%defattr(-,root,root,-) +%(pkg-config xorg-server --variable=moduledir )/input/evdev_drv.so + +%files devel +%defattr(-,root,root,-) +%{_includedir}/xorg/evdev-properties.h +%{_libdir}/pkgconfig/xorg-evdev.pc + diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..2b6c800 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,40 @@ +# Copyright 2005 Adam Jackson. +# +# 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 +# on the rights to use, copy, modify, merge, publish, distribute, sub +# license, 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 (including the next +# paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL +# ADAM JACKSON 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. + + +# this is obnoxious: +# -module lets us name the module exactly how we want +# -avoid-version prevents gratuitous .0.0.0 version numbers on the end +# _ladir passes a dummy rpath to libtool so the thing will actually link +# TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc. +AM_CFLAGS = $(XORG_CFLAGS) + +@DRIVER_NAME@_drv_la_LTLIBRARIES = @DRIVER_NAME@_drv.la +@DRIVER_NAME@_drv_la_LDFLAGS = -module -avoid-version +@DRIVER_NAME@_drv_ladir = @inputdir@ + +INCLUDES=-I$(top_srcdir)/include/ + +@DRIVER_NAME@_drv_la_SOURCES = @DRIVER_NAME@.c \ + @DRIVER_NAME@.h \ + emuMB.c \ + emuWheel.c \ + draglock.c + diff --git a/src/draglock.c b/src/draglock.c new file mode 100644 index 0000000..6157cae --- /dev/null +++ b/src/draglock.c @@ -0,0 +1,321 @@ +/* + * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. + * Copyright 1993 by David Dawes <dawes@xfree86.org> + * Copyright 2002 by SuSE Linux AG, Author: Egbert Eich + * Copyright 1994-2002 by The XFree86 Project, Inc. + * Copyright 2002 by Paul Elliott + * (Ported from xf86-input-mouse, above copyrights taken from there) + * Copyright © 2008 University of South Australia + * Copyright 2008 by Chris Salch + * Copyright 2008 Red Hat, Inc. + * + * 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 the authors + * not be used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. The authors make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS 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. + * + */ + +/* Draglock code */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <xf86.h> +#include <xf86Xinput.h> +#include <X11/Xatom.h> +#include <exevents.h> + +#include <evdev-properties.h> +#include "evdev.h" + +#ifdef HAVE_PROPERTIES +static Atom prop_dlock = 0; /* Drag lock buttons. */ +#endif + +void EvdevDragLockLockButton(InputInfoPtr pInfo, unsigned int button); + + +/* Setup and configuration code */ +void +EvdevDragLockPreInit(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + char *option_string = NULL; + int meta_button = 0; + int lock_button = 0; + char *next_num = NULL; + char *end_str = NULL; + BOOL pairs = FALSE; + + option_string = xf86CheckStrOption(pInfo->options, "DragLockButtons",NULL); + + if (!option_string) + return; + + next_num = option_string; + + /* Loop until we hit the end of our option string */ + while (next_num != NULL) { + lock_button = 0; + meta_button = strtol(next_num, &end_str, 10); + + /* check to see if we found anything */ + if (next_num != end_str) { + /* setup for the next number */ + next_num = end_str; + } else { + /* we have nothing more to parse, drop out of the loop */ + next_num = NULL; + } + + /* Check for a button to lock if we have a meta button */ + if (meta_button != 0 && next_num != NULL ) { + lock_button = strtol(next_num, &end_str, 10); + + /* check to see if we found anything */ + if (next_num != end_str) { + /* setup for the next number */ + next_num = end_str; + } else { + /* we have nothing more to parse, drop out of the loop */ + next_num = NULL; + } + } + + /* Ok, let the user know what we found on this look */ + if (meta_button != 0) { + if (lock_button == 0) { + if (!pairs) { + /* We only have a meta button */ + pEvdev->dragLock.meta = meta_button; + + xf86Msg(X_CONFIG, "%s: DragLockButtons : " + "%i as meta\n", + pInfo->name, meta_button); + } else { + xf86Msg(X_ERROR, "%s: DragLockButtons : " + "Incomplete pair specifying button pairs %s\n", + pInfo->name, option_string); + } + } else { + + /* Do bounds checking to make sure we don't crash */ + if ((meta_button <= EVDEV_MAXBUTTONS) && (meta_button >= 0 ) && + (lock_button <= EVDEV_MAXBUTTONS) && (lock_button >= 0)) { + + xf86Msg(X_CONFIG, "%s: DragLockButtons : %i -> %i\n", + pInfo->name, meta_button, lock_button); + + pEvdev->dragLock.lock_pair[meta_button - 1] = lock_button; + pairs=TRUE; + } else { + /* Let the user know something was wrong + with this pair of buttons */ + xf86Msg(X_CONFIG, "%s: DragLockButtons : " + "Invalid button pair %i -> %i\n", + pInfo->name, meta_button, lock_button); + } + } + } else { + xf86Msg(X_ERROR, "%s: Found DragLockButtons " + "with invalid lock button string : '%s'\n", + pInfo->name, option_string); + + /* This should be the case anyhow, just make sure */ + next_num = NULL; + } + + /* Check for end of string, to avoid annoying error */ + if (next_num != NULL && *next_num == '\0') + next_num = NULL; + } +} + +/* Updates DragLock button state and fires button event messges */ +void +EvdevDragLockLockButton(InputInfoPtr pInfo, unsigned int button) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + BOOL state = 0; + + /* update button state */ + state = pEvdev->dragLock.lock_state[button - 1] ? FALSE : TRUE; + pEvdev->dragLock.lock_state[button - 1] = state; + + EvdevQueueButtonEvent(pInfo, button, state); +} + +/* Filter button presses looking for either a meta button or the + * control of a button pair. + * + * @param button button number (1 for left, 3 for right) + * @param value TRUE if button press, FALSE if release + * + * @return TRUE if the event was swallowed here, FALSE otherwise. + */ +BOOL +EvdevDragLockFilterEvent(InputInfoPtr pInfo, unsigned int button, int value) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + + if (button == 0) + return FALSE; + + /* Do we have a single meta key or + several button pairings? */ + if (pEvdev->dragLock.meta != 0) { + + if (pEvdev->dragLock.meta == button) { + + /* setup up for button lock */ + if (value) + pEvdev->dragLock.meta_state = TRUE; + + return TRUE; + } else if (pEvdev->dragLock.meta_state) { /* waiting to lock */ + + pEvdev->dragLock.meta_state = FALSE; + + EvdevDragLockLockButton(pInfo, button); + + return TRUE; + } + } else if (pEvdev->dragLock.lock_pair[button - 1] && value) { + /* A meta button in a meta/lock pair was pressed */ + EvdevDragLockLockButton(pInfo, pEvdev->dragLock.lock_pair[button - 1]); + return TRUE; + } + + /* Eat events for buttons that are locked */ + if (pEvdev->dragLock.lock_state[button - 1]) + return TRUE; + + return FALSE; +} + +#ifdef HAVE_PROPERTIES +/** + * Set the drag lock property. + * If only one value is supplied, then this is used as the meta button. + * If more than one value is supplied, then each value is the drag lock button + * for the pair. 0 disables a pair. + * i.e. to set bt 3 to draglock button 1, supply 0,0,1 + */ +static int +EvdevDragLockSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, + BOOL checkonly) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + + if (atom == prop_dlock) + { + int i; + + if (val->format != 8 || val->type != XA_INTEGER) + return BadMatch; + + /* Don't allow changes while a lock is active */ + if (pEvdev->dragLock.meta) + { + if (pEvdev->dragLock.meta_state) + return BadAccess; + } else + { + for (i = 0; i < EVDEV_MAXBUTTONS; i++) + if (pEvdev->dragLock.lock_state[i]) + return BadValue; + } + + if (val->size == 0) + return BadMatch; + else if (val->size == 1) + { + int meta = *((CARD8*)val->data); + if (meta > EVDEV_MAXBUTTONS) + return BadValue; + + if (!checkonly) + { + pEvdev->dragLock.meta = meta; + memset(pEvdev->dragLock.lock_pair, 0, sizeof(pEvdev->dragLock.lock_pair)); + } + } else if ((val->size % 2) == 0) + { + CARD8* vals = (CARD8*)val->data; + + for (i = 0; i < val->size && i < EVDEV_MAXBUTTONS; i++) + if (vals[i] > EVDEV_MAXBUTTONS) + return BadValue; + + if (!checkonly) + { + pEvdev->dragLock.meta = 0; + memset(pEvdev->dragLock.lock_pair, 0, sizeof(pEvdev->dragLock.lock_pair)); + + for (i = 0; i < val->size && i < EVDEV_MAXBUTTONS; i += 2) + pEvdev->dragLock.lock_pair[vals[i] - 1] = vals[i + 1]; + } + } else + return BadMatch; + } + + return Success; +} + +/** + * Initialise property for drag lock buttons setting. + */ +void +EvdevDragLockInitProperty(DeviceIntPtr dev) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + + if (!dev->button) /* don't init prop for keyboards */ + return; + + prop_dlock = MakeAtom(EVDEV_PROP_DRAGLOCK, strlen(EVDEV_PROP_DRAGLOCK), TRUE); + if (pEvdev->dragLock.meta) + { + XIChangeDeviceProperty(dev, prop_dlock, XA_INTEGER, 8, + PropModeReplace, 1, &pEvdev->dragLock.meta, + FALSE); + } else { + int highest = 0; + int i; + CARD8 pair[EVDEV_MAXBUTTONS] = {0}; + + for (i = 0; i < EVDEV_MAXBUTTONS; i++) + { + if (pEvdev->dragLock.lock_pair[i]) + highest = i; + pair[i] = pEvdev->dragLock.lock_pair[i]; + } + + XIChangeDeviceProperty(dev, prop_dlock, XA_INTEGER, 8, PropModeReplace, + highest + 1, pair, FALSE); + } + + XISetDevicePropertyDeletable(dev, prop_dlock, FALSE); + + XIRegisterPropertyHandler(dev, EvdevDragLockSetProperty, NULL, NULL); +} + +#endif diff --git a/src/emuMB.c b/src/emuMB.c new file mode 100644 index 0000000..199c0d7 --- /dev/null +++ b/src/emuMB.c @@ -0,0 +1,421 @@ +/* + * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. + * Copyright 1993 by David Dawes <dawes@xfree86.org> + * Copyright 2002 by SuSE Linux AG, Author: Egbert Eich + * Copyright 1994-2002 by The XFree86 Project, Inc. + * Copyright 2002 by Paul Elliott + * (Ported from xf86-input-mouse, above copyrights taken from there) + * Copyright © 2008 University of South Australia + * + * 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 the authors + * not be used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. The authors make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS 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. + * + */ + +/* Middle mouse button emulation code. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <X11/Xatom.h> +#include <xf86.h> +#include <xf86Xinput.h> +#include <exevents.h> + +#include <evdev-properties.h> +#include "evdev.h" + +enum { + MBEMU_DISABLED = 0, + MBEMU_ENABLED, + MBEMU_AUTO +}; + +#ifdef HAVE_PROPERTIES +static Atom prop_mbemu = 0; /* Middle button emulation on/off property */ +static Atom prop_mbtimeout = 0; /* Middle button timeout property */ +#endif +/* + * Lets create a simple finite-state machine for 3 button emulation: + * + * We track buttons 1 and 3 (left and right). There are 11 states: + * 0 ground - initial state + * 1 delayed left - left pressed, waiting for right + * 2 delayed right - right pressed, waiting for left + * 3 pressed middle - right and left pressed, emulated middle sent + * 4 pressed left - left pressed and sent + * 5 pressed right - right pressed and sent + * 6 released left - left released after emulated middle + * 7 released right - right released after emulated middle + * 8 repressed left - left pressed after released left + * 9 repressed right - right pressed after released right + * 10 pressed both - both pressed, not emulating middle + * + * At each state, we need handlers for the following events + * 0: no buttons down + * 1: left button down + * 2: right button down + * 3: both buttons down + * 4: emulate3Timeout passed without a button change + * Note that button events are not deltas, they are the set of buttons being + * pressed now. It's possible (ie, mouse hardware does it) to go from (eg) + * left down to right down without anything in between, so all cases must be + * handled. + * + * a handler consists of three values: + * 0: action1 + * 1: action2 + * 2: new emulation state + * + * action > 0: ButtonPress + * action = 0: nothing + * action < 0: ButtonRelease + * + * The comment preceeding each section is the current emulation state. + * The comments to the right are of the form + * <button state> (<events>) -> <new emulation state> + * which should be read as + * If the buttons are in <button state>, generate <events> then go to + * <new emulation state>. + */ +static signed char stateTab[11][5][3] = { +/* 0 ground */ + { + { 0, 0, 0 }, /* nothing -> ground (no change) */ + { 0, 0, 1 }, /* left -> delayed left */ + { 0, 0, 2 }, /* right -> delayed right */ + { 2, 0, 3 }, /* left & right (middle press) -> pressed middle */ + { 0, 0, -1 } /* timeout N/A */ + }, +/* 1 delayed left */ + { + { 1, -1, 0 }, /* nothing (left event) -> ground */ + { 0, 0, 1 }, /* left -> delayed left (no change) */ + { 1, -1, 2 }, /* right (left event) -> delayed right */ + { 2, 0, 3 }, /* left & right (middle press) -> pressed middle */ + { 1, 0, 4 }, /* timeout (left press) -> pressed left */ + }, +/* 2 delayed right */ + { + { 3, -3, 0 }, /* nothing (right event) -> ground */ + { 3, -3, 1 }, /* left (right event) -> delayed left (no change) */ + { 0, 0, 2 }, /* right -> delayed right (no change) */ + { 2, 0, 3 }, /* left & right (middle press) -> pressed middle */ + { 3, 0, 5 }, /* timeout (right press) -> pressed right */ + }, +/* 3 pressed middle */ + { + { -2, 0, 0 }, /* nothing (middle release) -> ground */ + { 0, 0, 7 }, /* left -> released right */ + { 0, 0, 6 }, /* right -> released left */ + { 0, 0, 3 }, /* left & right -> pressed middle (no change) */ + { 0, 0, -1 }, /* timeout N/A */ + }, +/* 4 pressed left */ + { + { -1, 0, 0 }, /* nothing (left release) -> ground */ + { 0, 0, 4 }, /* left -> pressed left (no change) */ + { -1, 0, 2 }, /* right (left release) -> delayed right */ + { 3, 0, 10 }, /* left & right (right press) -> pressed both */ + { 0, 0, -1 }, /* timeout N/A */ + }, +/* 5 pressed right */ + { + { -3, 0, 0 }, /* nothing (right release) -> ground */ + { -3, 0, 1 }, /* left (right release) -> delayed left */ + { 0, 0, 5 }, /* right -> pressed right (no change) */ + { 1, 0, 10 }, /* left & right (left press) -> pressed both */ + { 0, 0, -1 }, /* timeout N/A */ + }, +/* 6 released left */ + { + { -2, 0, 0 }, /* nothing (middle release) -> ground */ + { -2, 0, 1 }, /* left (middle release) -> delayed left */ + { 0, 0, 6 }, /* right -> released left (no change) */ + { 1, 0, 8 }, /* left & right (left press) -> repressed left */ + { 0, 0, -1 }, /* timeout N/A */ + }, +/* 7 released right */ + { + { -2, 0, 0 }, /* nothing (middle release) -> ground */ + { 0, 0, 7 }, /* left -> released right (no change) */ + { -2, 0, 2 }, /* right (middle release) -> delayed right */ + { 3, 0, 9 }, /* left & right (right press) -> repressed right */ + { 0, 0, -1 }, /* timeout N/A */ + }, +/* 8 repressed left */ + { + { -2, -1, 0 }, /* nothing (middle release, left release) -> ground */ + { -2, 0, 4 }, /* left (middle release) -> pressed left */ + { -1, 0, 6 }, /* right (left release) -> released left */ + { 0, 0, 8 }, /* left & right -> repressed left (no change) */ + { 0, 0, -1 }, /* timeout N/A */ + }, +/* 9 repressed right */ + { + { -2, -3, 0 }, /* nothing (middle release, right release) -> ground */ + { -3, 0, 7 }, /* left (right release) -> released right */ + { -2, 0, 5 }, /* right (middle release) -> pressed right */ + { 0, 0, 9 }, /* left & right -> repressed right (no change) */ + { 0, 0, -1 }, /* timeout N/A */ + }, +/* 10 pressed both */ + { + { -1, -3, 0 }, /* nothing (left release, right release) -> ground */ + { -3, 0, 4 }, /* left (right release) -> pressed left */ + { -1, 0, 5 }, /* right (left release) -> pressed right */ + { 0, 0, 10 }, /* left & right -> pressed both (no change) */ + { 0, 0, -1 }, /* timeout N/A */ + }, +}; + + +int +EvdevMBEmuTimer(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = pInfo->private; + int sigstate; + int id; + + sigstate = xf86BlockSIGIO (); + + pEvdev->emulateMB.pending = FALSE; + if ((id = stateTab[pEvdev->emulateMB.state][4][0]) != 0) { + EvdevPostButtonEvent(pInfo, abs(id), (id >= 0)); + pEvdev->emulateMB.state = + stateTab[pEvdev->emulateMB.state][4][2]; + } else { + ErrorF("Got unexpected buttonTimer in state %d\n", + pEvdev->emulateMB.state); + } + + xf86UnblockSIGIO (sigstate); + return 0; +} + + +/** + * Emulate a middle button on button press. + * + * @param code button number (1 for left, 3 for right) + * @param press TRUE if press, FALSE if release. + * + * @return TRUE if event was swallowed by middle mouse button emulation, FALSE + * otherwise. + */ +BOOL +EvdevMBEmuFilterEvent(InputInfoPtr pInfo, int button, BOOL press) +{ + EvdevPtr pEvdev = pInfo->private; + int id; + int *btstate; + int ret = FALSE; + + if (!pEvdev->emulateMB.enabled) + return ret; + + if (button == 2) { + EvdevMBEmuEnable(pInfo, FALSE); + return ret; + } + + /* don't care about other buttons */ + if (button != 1 && button != 3) + return ret; + + btstate = &pEvdev->emulateMB.buttonstate; + if (press) + *btstate |= (button == 1) ? 0x1 : 0x2; + else + *btstate &= (button == 1) ? ~0x1 : ~0x2; + + if ((id = stateTab[pEvdev->emulateMB.state][*btstate][0]) != 0) + { + EvdevQueueButtonEvent(pInfo, abs(id), (id >= 0)); + ret = TRUE; + } + if ((id = stateTab[pEvdev->emulateMB.state][*btstate][1]) != 0) + { + EvdevQueueButtonEvent(pInfo, abs(id), (id >= 0)); + ret = TRUE; + } + + pEvdev->emulateMB.state = + stateTab[pEvdev->emulateMB.state][*btstate][2]; + + if (stateTab[pEvdev->emulateMB.state][4][0] != 0) { + pEvdev->emulateMB.expires = GetTimeInMillis () + pEvdev->emulateMB.timeout; + pEvdev->emulateMB.pending = TRUE; + ret = TRUE; + } else { + pEvdev->emulateMB.pending = FALSE; + } + + return ret; +} + + +void EvdevMBEmuWakeupHandler(pointer data, + int i, + pointer LastSelectMask) +{ + InputInfoPtr pInfo = (InputInfoPtr)data; + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + int ms; + + if (pEvdev->emulateMB.pending) + { + ms = pEvdev->emulateMB.expires - GetTimeInMillis(); + if (ms <= 0) + EvdevMBEmuTimer(pInfo); + } +} + +void EvdevMBEmuBlockHandler(pointer data, + struct timeval **waitTime, + pointer LastSelectMask) +{ + InputInfoPtr pInfo = (InputInfoPtr) data; + EvdevPtr pEvdev= (EvdevPtr) pInfo->private; + int ms; + + if (pEvdev->emulateMB.pending) + { + ms = pEvdev->emulateMB.expires - GetTimeInMillis (); + if (ms <= 0) + ms = 0; + AdjustWaitForDelay (waitTime, ms); + } +} + +void +EvdevMBEmuPreInit(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + pEvdev->emulateMB.enabled = MBEMU_AUTO; + + if (xf86FindOption(pInfo->options, "Emulate3Buttons")) + { + pEvdev->emulateMB.enabled = xf86SetBoolOption(pInfo->options, + "Emulate3Buttons", + MBEMU_ENABLED); + xf86Msg(X_INFO, "%s: Forcing middle mouse button emulation %s.\n", + pInfo->name, (pEvdev->emulateMB.enabled) ? "on" : "off"); + } + + pEvdev->emulateMB.timeout = xf86SetIntOption(pInfo->options, + "Emulate3Timeout", 50); +} + +void +EvdevMBEmuOn(InputInfoPtr pInfo) +{ + if (!pInfo->dev->button) /* don't init for keyboards */ + return; + + RegisterBlockAndWakeupHandlers (EvdevMBEmuBlockHandler, + EvdevMBEmuWakeupHandler, + (pointer)pInfo); +} + +void +EvdevMBEmuFinalize(InputInfoPtr pInfo) +{ + if (!pInfo->dev->button) /* don't cleanup for keyboards */ + return; + + RemoveBlockAndWakeupHandlers (EvdevMBEmuBlockHandler, + EvdevMBEmuWakeupHandler, + (pointer)pInfo); + +} + +/* Enable/disable middle mouse button emulation. */ +void +EvdevMBEmuEnable(InputInfoPtr pInfo, BOOL enable) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + if (pEvdev->emulateMB.enabled == MBEMU_AUTO) + pEvdev->emulateMB.enabled = enable; +} + + +#ifdef HAVE_PROPERTIES +static int +EvdevMBEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, + BOOL checkonly) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + + if (atom == prop_mbemu) + { + if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER) + return BadMatch; + + if (!checkonly) + pEvdev->emulateMB.enabled = *((BOOL*)val->data); + } else if (atom == prop_mbtimeout) + { + if (val->format != 32 || val->size != 1 || val->type != XA_INTEGER) + return BadMatch; + + if (!checkonly) + pEvdev->emulateMB.timeout = *((CARD32*)val->data); + } + + return Success; +} + +/** + * Initialise property for MB emulation on/off. + */ +void +EvdevMBEmuInitProperty(DeviceIntPtr dev) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + int rc; + + if (!dev->button) /* don't init prop for keyboards */ + return; + + prop_mbemu = MakeAtom(EVDEV_PROP_MIDBUTTON, strlen(EVDEV_PROP_MIDBUTTON), TRUE); + rc = XIChangeDeviceProperty(dev, prop_mbemu, XA_INTEGER, 8, + PropModeReplace, 1, + &pEvdev->emulateMB.enabled, + FALSE); + if (rc != Success) + return; + XISetDevicePropertyDeletable(dev, prop_mbemu, FALSE); + + prop_mbtimeout = MakeAtom(EVDEV_PROP_MIDBUTTON_TIMEOUT, + strlen(EVDEV_PROP_MIDBUTTON_TIMEOUT), + TRUE); + rc = XIChangeDeviceProperty(dev, prop_mbtimeout, XA_INTEGER, 32, PropModeReplace, 1, + &pEvdev->emulateMB.timeout, FALSE); + + if (rc != Success) + return; + XISetDevicePropertyDeletable(dev, prop_mbtimeout, FALSE); + + XIRegisterPropertyHandler(dev, EvdevMBEmuSetProperty, NULL, NULL); +} +#endif diff --git a/src/emuWheel.c b/src/emuWheel.c new file mode 100644 index 0000000..9be4eb9 --- /dev/null +++ b/src/emuWheel.c @@ -0,0 +1,478 @@ +/* +* Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. +* Copyright 1993 by David Dawes <dawes@xfree86.org> +* Copyright 2002 by SuSE Linux AG, Author: Egbert Eich +* Copyright 1994-2002 by The XFree86 Project, Inc. +* Copyright 2002 by Paul Elliott +* (Ported from xf86-input-mouse, above copyrights taken from there) +* Copyright 2008 by Chris Salch +* Copyright © 2008 Red Hat, Inc. +* +* 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 the authors +* not be used in advertising or publicity pertaining to distribution of the +* software without specific, written prior permission. The authors make no +* representations about the suitability of this software for any +* purpose. It is provided "as is" without express or implied +* warranty. +* +* THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN +* NO EVENT SHALL THE AUTHORS 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. +* +*/ + +/* Mouse wheel emulation code. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <X11/Xatom.h> +#include <xf86.h> +#include <xf86Xinput.h> +#include <exevents.h> + +#include <evdev-properties.h> +#include "evdev.h" + +#define WHEEL_NOT_CONFIGURED 0 + +#ifdef HAVE_PROPERTIES +static Atom prop_wheel_emu = 0; +static Atom prop_wheel_axismap = 0; +static Atom prop_wheel_inertia = 0; +static Atom prop_wheel_timeout = 0; +static Atom prop_wheel_button = 0; +#endif + +/* Local Funciton Prototypes */ +static BOOL EvdevWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char *axis_name); +static int EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value); + +/* Filter mouse button events */ +BOOL +EvdevWheelEmuFilterButton(InputInfoPtr pInfo, unsigned int button, int value) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + int ms; + + /* Has wheel emulation been configured to be enabled? */ + if (!pEvdev->emulateWheel.enabled) + return FALSE; + + /* Check for EmulateWheelButton */ + if (pEvdev->emulateWheel.button == button) { + pEvdev->emulateWheel.button_state = value; + + if (value) + /* Start the timer when the button is pressed */ + pEvdev->emulateWheel.expires = pEvdev->emulateWheel.timeout + + GetTimeInMillis(); + else { + ms = pEvdev->emulateWheel.expires - GetTimeInMillis(); + if (ms > 0) { + /* + * If the button is released early enough emit the button + * press/release events + */ + EvdevQueueButtonClicks(pInfo, button, 1); + } + } + + return TRUE; + } + + /* Don't care about this event */ + return FALSE; +} + +/* Filter mouse wheel events */ +BOOL +EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + WheelAxisPtr pAxis = NULL, pOtherAxis = NULL; + int value = pEv->value; + + /* Has wheel emulation been configured to be enabled? */ + if (!pEvdev->emulateWheel.enabled) + return FALSE; + + /* Handle our motion events if the emuWheel button is pressed + * wheel button of 0 means always emulate wheel. + */ + if (pEvdev->emulateWheel.button_state || !pEvdev->emulateWheel.button) { + /* Just return if the timeout hasn't expired yet */ + if (pEvdev->emulateWheel.button) + { + int ms = pEvdev->emulateWheel.expires - GetTimeInMillis(); + if (ms > 0) + return TRUE; + } + + /* We don't want to intercept real mouse wheel events */ + switch(pEv->code) { + case REL_X: + pAxis = &(pEvdev->emulateWheel.X); + pOtherAxis = &(pEvdev->emulateWheel.Y); + break; + + case REL_Y: + pAxis = &(pEvdev->emulateWheel.Y); + pOtherAxis = &(pEvdev->emulateWheel.X); + break; + + default: + break; + } + + /* If we found REL_X or REL_Y, emulate a mouse wheel. + Reset the inertia of the other axis when a scroll event was sent + to avoid the buildup of erroneous scroll events if the user + doesn't move in a perfectly straight line. + */ + if (pAxis) + { + if (EvdevWheelEmuInertia(pInfo, pAxis, value)) + pOtherAxis->traveled_distance = 0; + } + + /* Eat motion events while emulateWheel button pressed. */ + return TRUE; + } + + return FALSE; +} + +/* Simulate inertia for our emulated mouse wheel. + Returns the number of wheel events generated. + */ +static int +EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + int button; + int inertia; + int rc = 0; + + /* if this axis has not been configured, just eat the motion */ + if (!axis->up_button) + return rc; + + axis->traveled_distance += value; + + if (axis->traveled_distance < 0) { + button = axis->up_button; + inertia = -pEvdev->emulateWheel.inertia; + } else { + button = axis->down_button; + inertia = pEvdev->emulateWheel.inertia; + } + + /* Produce button press events for wheel motion */ + while(abs(axis->traveled_distance) > pEvdev->emulateWheel.inertia) { + axis->traveled_distance -= inertia; + EvdevQueueButtonClicks(pInfo, button, 1); + rc++; + } + return rc; +} + +/* Handle button mapping here to avoid code duplication, +returns true if a button mapping was found. */ +static BOOL +EvdevWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char* axis_name) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + char *option_string; + + pAxis->up_button = WHEEL_NOT_CONFIGURED; + + /* Check to see if there is configuration for this axis */ + option_string = xf86SetStrOption(pInfo->options, axis_name, NULL); + if (option_string) { + int up_button = 0; + int down_button = 0; + char *msg = NULL; + + if ((sscanf(option_string, "%d %d", &up_button, &down_button) == 2) && + ((up_button > 0) && (up_button <= EVDEV_MAXBUTTONS)) && + ((down_button > 0) && (down_button <= EVDEV_MAXBUTTONS))) { + + /* Use xstrdup to allocate a string for us */ + msg = xstrdup("buttons XX and YY"); + + if (msg) + sprintf(msg, "buttons %d and %d", up_button, down_button); + + pAxis->up_button = up_button; + pAxis->down_button = down_button; + + /* Update the number of buttons if needed */ + if (up_button > pEvdev->num_buttons) pEvdev->num_buttons = up_button; + if (down_button > pEvdev->num_buttons) pEvdev->num_buttons = down_button; + + } else { + xf86Msg(X_WARNING, "%s: Invalid %s value:\"%s\"\n", + pInfo->name, axis_name, option_string); + + } + + /* Clean up and log what happened */ + if (msg) { + xf86Msg(X_CONFIG, "%s: %s: %s\n",pInfo->name, axis_name, msg); + free(msg); + return TRUE; + } + } + return FALSE; +} + +/* Setup the basic configuration options used by mouse wheel emulation */ +void +EvdevWheelEmuPreInit(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + char val[4]; + int wheelButton; + int inertia; + int timeout; + + val[0] = 0; + val[1] = 0; + + if (xf86SetBoolOption(pInfo->options, "EmulateWheel", FALSE)) { + pEvdev->emulateWheel.enabled = TRUE; + } else + pEvdev->emulateWheel.enabled = FALSE; + + wheelButton = xf86SetIntOption(pInfo->options, "EmulateWheelButton", 4); + + if ((wheelButton < 0) || (wheelButton > EVDEV_MAXBUTTONS)) { + xf86Msg(X_WARNING, "%s: Invalid EmulateWheelButton value: %d\n", + pInfo->name, wheelButton); + xf86Msg(X_WARNING, "%s: Wheel emulation disabled.\n", pInfo->name); + + pEvdev->emulateWheel.enabled = FALSE; + } + + pEvdev->emulateWheel.button = wheelButton; + + inertia = xf86SetIntOption(pInfo->options, "EmulateWheelInertia", 10); + + if (inertia <= 0) { + xf86Msg(X_WARNING, "%s: Invalid EmulateWheelInertia value: %d\n", + pInfo->name, inertia); + xf86Msg(X_WARNING, "%s: Using built-in inertia value.\n", + pInfo->name); + + inertia = 10; + } + + pEvdev->emulateWheel.inertia = inertia; + + timeout = xf86SetIntOption(pInfo->options, "EmulateWheelTimeout", 200); + + if (timeout < 0) { + xf86Msg(X_WARNING, "%s: Invalid EmulateWheelTimeout value: %d\n", + pInfo->name, timeout); + xf86Msg(X_WARNING, "%s: Using built-in timeout value.\n", + pInfo->name); + + timeout = 200; + } + + pEvdev->emulateWheel.timeout = timeout; + + /* Configure the Y axis or default it */ + if (!EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.Y), + "YAxisMapping")) { + /* Default the Y axis to sane values */ + pEvdev->emulateWheel.Y.up_button = 4; + pEvdev->emulateWheel.Y.down_button = 5; + + /* Simpler to check just the largest value in this case */ + /* XXX: we should post this to the server */ + if (5 > pEvdev->num_buttons) + pEvdev->num_buttons = 5; + + /* Display default Configuration */ + xf86Msg(X_CONFIG, "%s: YAxisMapping: buttons %d and %d\n", + pInfo->name, pEvdev->emulateWheel.Y.up_button, + pEvdev->emulateWheel.Y.down_button); + } + + + /* This axis should default to an unconfigured state as most people + are not going to expect a Horizontal wheel. */ + EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.X), + "XAxisMapping"); + + /* Used by the inertia code */ + pEvdev->emulateWheel.X.traveled_distance = 0; + pEvdev->emulateWheel.Y.traveled_distance = 0; + + xf86Msg(X_CONFIG, "%s: EmulateWheelButton: %d, " + "EmulateWheelInertia: %d, " + "EmulateWheelTimeout: %d\n", + pInfo->name, pEvdev->emulateWheel.button, inertia, timeout); +} + +#ifdef HAVE_PROPERTIES +static int +EvdevWheelEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, + BOOL checkonly) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + + if (atom == prop_wheel_emu) + { + if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER) + return BadMatch; + + if (!checkonly) + { + pEvdev->emulateWheel.enabled = *((BOOL*)val->data); + /* Don't enable with zero inertia, otherwise we may get stuck in an + * infinite loop */ + if (pEvdev->emulateWheel.inertia <= 0) + { + pEvdev->emulateWheel.inertia = 10; + /* We may get here before the property is actually enabled */ + if (prop_wheel_inertia) + XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER, + 16, PropModeReplace, 1, + &pEvdev->emulateWheel.inertia, TRUE); + } + } + } + else if (atom == prop_wheel_button) + { + int bt; + + if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER) + return BadMatch; + + bt = *((CARD8*)val->data); + + if (bt < 0 || bt >= EVDEV_MAXBUTTONS) + return BadValue; + + if (!checkonly) + pEvdev->emulateWheel.button = bt; + } else if (atom == prop_wheel_axismap) + { + if (val->format != 8 || val->size != 4 || val->type != XA_INTEGER) + return BadMatch; + + if (!checkonly) + { + pEvdev->emulateWheel.X.up_button = *((CARD8*)val->data); + pEvdev->emulateWheel.X.down_button = *(((CARD8*)val->data) + 1); + pEvdev->emulateWheel.Y.up_button = *(((CARD8*)val->data) + 2); + pEvdev->emulateWheel.Y.down_button = *(((CARD8*)val->data) + 3); + } + } else if (atom == prop_wheel_inertia) + { + int inertia; + + if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER) + return BadMatch; + + inertia = *((CARD16*)val->data); + + if (inertia < 0) + return BadValue; + + if (!checkonly) + pEvdev->emulateWheel.inertia = inertia; + } else if (atom == prop_wheel_timeout) + { + int timeout; + + if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER) + return BadMatch; + + timeout = *((CARD16*)val->data); + + if (timeout < 0) + return BadValue; + + if (!checkonly) + pEvdev->emulateWheel.timeout = timeout; + } + return Success; +} + +void +EvdevWheelEmuInitProperty(DeviceIntPtr dev) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + int rc = TRUE; + char vals[4]; + + if (!dev->button) /* don't init prop for keyboards */ + return; + + prop_wheel_emu = MakeAtom(EVDEV_PROP_WHEEL, strlen(EVDEV_PROP_WHEEL), TRUE); + rc = XIChangeDeviceProperty(dev, prop_wheel_emu, XA_INTEGER, 8, + PropModeReplace, 1, + &pEvdev->emulateWheel.enabled, FALSE); + if (rc != Success) + return; + + XISetDevicePropertyDeletable(dev, prop_wheel_emu, FALSE); + + vals[0] = pEvdev->emulateWheel.X.up_button; + vals[1] = pEvdev->emulateWheel.X.down_button; + vals[2] = pEvdev->emulateWheel.Y.up_button; + vals[3] = pEvdev->emulateWheel.Y.down_button; + + prop_wheel_axismap = MakeAtom(EVDEV_PROP_WHEEL_AXES, strlen(EVDEV_PROP_WHEEL_AXES), TRUE); + rc = XIChangeDeviceProperty(dev, prop_wheel_axismap, XA_INTEGER, 8, + PropModeReplace, 4, vals, FALSE); + + if (rc != Success) + return; + + XISetDevicePropertyDeletable(dev, prop_wheel_axismap, FALSE); + + prop_wheel_inertia = MakeAtom(EVDEV_PROP_WHEEL_INERTIA, strlen(EVDEV_PROP_WHEEL_INERTIA), TRUE); + rc = XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER, 16, + PropModeReplace, 1, + &pEvdev->emulateWheel.inertia, FALSE); + if (rc != Success) + return; + + XISetDevicePropertyDeletable(dev, prop_wheel_inertia, FALSE); + + prop_wheel_timeout = MakeAtom(EVDEV_PROP_WHEEL_TIMEOUT, strlen(EVDEV_PROP_WHEEL_TIMEOUT), TRUE); + rc = XIChangeDeviceProperty(dev, prop_wheel_timeout, XA_INTEGER, 16, + PropModeReplace, 1, + &pEvdev->emulateWheel.timeout, FALSE); + if (rc != Success) + return; + + XISetDevicePropertyDeletable(dev, prop_wheel_timeout, FALSE); + + prop_wheel_button = MakeAtom(EVDEV_PROP_WHEEL_BUTTON, strlen(EVDEV_PROP_WHEEL_BUTTON), TRUE); + rc = XIChangeDeviceProperty(dev, prop_wheel_button, XA_INTEGER, 8, + PropModeReplace, 1, + &pEvdev->emulateWheel.button, FALSE); + if (rc != Success) + return; + + XISetDevicePropertyDeletable(dev, prop_wheel_button, FALSE); + + XIRegisterPropertyHandler(dev, EvdevWheelEmuSetProperty, NULL, NULL); +} +#endif diff --git a/src/evdev.c b/src/evdev.c new file mode 100755 index 0000000..e86c6db --- /dev/null +++ b/src/evdev.c @@ -0,0 +1,2699 @@ +/* + * Copyright © 2004-2008 Red Hat, Inc. + * + * 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 Red Hat + * not be used in advertising or publicity pertaining to distribution + * of the software without specific, written prior permission. Red + * Hat makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS 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. + * + * Authors: + * Kristian Høgsberg (krh@redhat.com) + * Adam Jackson (ajax@redhat.com) + * Peter Hutterer (peter.hutterer@redhat.com) + * Oliver McFadden (oliver.mcfadden@nokia.com) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <X11/keysym.h> + +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#include <xf86.h> +#include <xf86Xinput.h> +#include <exevents.h> +#include <xorgVersion.h> +#include <xkbsrv.h> + +#include "evdev.h" + +#ifdef HAVE_PROPERTIES +#include <X11/Xatom.h> +#include <evdev-properties.h> +#include <xserver-properties.h> +/* 1.6 has properties, but no labels */ +#ifdef AXIS_LABEL_PROP +#define HAVE_LABELS +#else +#undef HAVE_LABELS +#endif + +#endif + +#ifndef MAXDEVICES +#include <inputstr.h> /* for MAX_DEVICES */ +#define MAXDEVICES MAX_DEVICES +#endif + +/* 2.4 compatibility */ +#ifndef EVIOCGRAB +#define EVIOCGRAB _IOW('E', 0x90, int) +#endif + +#ifndef BTN_TASK +#define BTN_TASK 0x117 +#endif + +#ifndef EV_SYN +#define EV_SYN EV_RST +#endif +/* end compat */ + +#define ArrayLength(a) (sizeof(a) / (sizeof((a)[0]))) + +/* evdev flags */ +#define EVDEV_KEYBOARD_EVENTS (1 << 0) +#define EVDEV_BUTTON_EVENTS (1 << 1) +#define EVDEV_RELATIVE_EVENTS (1 << 2) +#define EVDEV_ABSOLUTE_EVENTS (1 << 3) +#define EVDEV_TOUCHPAD (1 << 4) +#define EVDEV_INITIALIZED (1 << 5) /* WheelInit etc. called already? */ +#define EVDEV_TOUCHSCREEN (1 << 6) +#define EVDEV_CALIBRATED (1 << 7) /* run-time calibrated? */ +#define EVDEV_TABLET (1 << 8) /* device looks like a tablet? */ +#define EVDEV_UNIGNORE_ABSOLUTE (1 << 9) /* explicitly unignore abs axes */ +#define EVDEV_UNIGNORE_RELATIVE (1 << 10) /* explicitly unignore rel axes */ +#define EVDEV_RESOLUTION (1 << 12) /* device looks like a multi-touch screen? */ + +#define MIN_KEYCODE 8 +#define GLYPHS_PER_KEY 2 +#define AltMask Mod1Mask +#define NumLockMask Mod2Mask +#define AltLangMask Mod3Mask +#define KanaMask Mod4Mask +#define ScrollLockMask Mod5Mask + +#define CAPSFLAG 1 +#define NUMFLAG 2 +#define SCROLLFLAG 4 +#define MODEFLAG 8 +#define COMPOSEFLAG 16 + +static const char *evdevDefaults[] = { + "XkbRules", "evdev", + "XkbModel", "evdev", + "XkbLayout", "us", + NULL +}; + +static int EvdevOn(DeviceIntPtr); +static int EvdevCacheCompare(InputInfoPtr pInfo, BOOL compare); +static void EvdevKbdCtrl(DeviceIntPtr device, KeybdCtrl *ctrl); +static void EvdevSwapAxes(EvdevPtr pEvdev); +static void EvdevSetResolution(InputInfoPtr pInfo, int num_resolution, int resolution[4]); + +#ifdef HAVE_PROPERTIES +static void EvdevInitAxesLabels(EvdevPtr pEvdev, int natoms, Atom *atoms); +static void EvdevInitButtonLabels(EvdevPtr pEvdev, int natoms, Atom *atoms); +static void EvdevInitProperty(DeviceIntPtr dev); +static int EvdevSetProperty(DeviceIntPtr dev, Atom atom, + XIPropertyValuePtr val, BOOL checkonly); +static void EvdevSetMouseExist(int value); +static void EvdevSetExtKeyboardExist(int value); +static Atom prop_invert = 0; +static Atom prop_reopen = 0; +static Atom prop_calibration = 0; +static Atom prop_swap = 0; +static Atom prop_axis_label = 0; +static Atom prop_btn_label = 0; +#endif + +/* All devices the evdev driver has allocated and knows about. + * MAXDEVICES is safe as null-terminated array, as two devices (VCP and VCK) + * cannot be used by evdev, leaving us with a space of 2 at the end. */ +static EvdevPtr evdev_devices[MAXDEVICES] = {NULL}; + +static size_t CountBits(unsigned long *array, size_t nlongs) +{ + unsigned int i; + size_t count = 0; + + for (i = 0; i < nlongs; i++) { + unsigned long x = array[i]; + + while (x > 0) + { + count += (x & 0x1); + x >>= 1; + } + } + return count; +} + +static int +EvdevGetMajorMinor(InputInfoPtr pInfo) +{ + struct stat st; + + if (fstat(pInfo->fd, &st) == -1) + { + xf86Msg(X_ERROR, "%s: stat failed (%s). cannot check for duplicates.\n", + pInfo->name, strerror(errno)); + return 0; + } + + return st.st_rdev; +} + +/** + * Return TRUE if one of the devices we know about has the same min/maj + * number. + */ +static BOOL +EvdevIsDuplicate(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = pInfo->private; + EvdevPtr* dev = evdev_devices; + + if (pEvdev->min_maj) + { + while(*dev) + { + if ((*dev) != pEvdev && + (*dev)->min_maj && + (*dev)->min_maj == pEvdev->min_maj) + return TRUE; + dev++; + } + } + return FALSE; +} + +/** + * Add to internal device list. + */ +static void +EvdevAddDevice(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = pInfo->private; + EvdevPtr* dev = evdev_devices; + + while(*dev) + dev++; + + *dev = pEvdev; +} + +/** + * Remove from internal device list. + */ +static void +EvdevRemoveDevice(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = pInfo->private; + EvdevPtr *dev = evdev_devices; + int count = 0; + + while(*dev) + { + count++; + if (*dev == pEvdev) + { + memmove(dev, dev + 1, + sizeof(evdev_devices) - (count * sizeof(EvdevPtr))); + break; + } + dev++; + } +} + + +static void +SetXkbOption(InputInfoPtr pInfo, char *name, char **option) +{ + char *s; + + if ((s = xf86SetStrOption(pInfo->options, name, NULL))) { + if (!s[0]) { + free(s); + *option = NULL; + } else { + *option = s; + } + } +} + +static int wheel_up_button = 4; +static int wheel_down_button = 5; +static int wheel_left_button = 6; +static int wheel_right_button = 7; + +void +EvdevQueueKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value) +{ + int code = ev->code + MIN_KEYCODE; + static char warned[KEY_CNT]; + EventQueuePtr pQueue; + EvdevPtr pEvdev = pInfo->private; + + /* Filter all repeated events from device. + We'll do softrepeat in the server, but only since 1.6 */ + if (value == 2 +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) <= 2 + && (ev->code == KEY_LEFTCTRL || ev->code == KEY_RIGHTCTRL || + ev->code == KEY_LEFTSHIFT || ev->code == KEY_RIGHTSHIFT || + ev->code == KEY_LEFTALT || ev->code == KEY_RIGHTALT || + ev->code == KEY_LEFTMETA || ev->code == KEY_RIGHTMETA || + ev->code == KEY_CAPSLOCK || ev->code == KEY_NUMLOCK || + ev->code == KEY_SCROLLLOCK) /* XXX windows keys? */ +#endif + ) + return; + + if (code > 255) + { + if (ev->code <= KEY_MAX && !warned[ev->code]) + { + xf86Msg(X_WARNING, "%s: unable to handle keycode %d\n", + pInfo->name, ev->code); + warned[ev->code] = 1; + } + + /* The X server can't handle keycodes > 255. */ + return; + } + + if (pEvdev->num_queue >= EVDEV_MAXQUEUE) + { + xf86Msg(X_NONE, "%s: dropping event due to full queue!\n", pInfo->name); + return; + } + + pQueue = &pEvdev->queue[pEvdev->num_queue]; + pQueue->type = EV_QUEUE_KEY; + pQueue->key = code; + pQueue->val = value; + pEvdev->num_queue++; +} + +void +EvdevQueueButtonEvent(InputInfoPtr pInfo, int button, int value) +{ + EventQueuePtr pQueue; + EvdevPtr pEvdev = pInfo->private; + + if (pEvdev->num_queue >= EVDEV_MAXQUEUE) + { + xf86Msg(X_NONE, "%s: dropping event due to full queue!\n", pInfo->name); + return; + } + + pQueue = &pEvdev->queue[pEvdev->num_queue]; + pQueue->type = EV_QUEUE_BTN; + pQueue->key = button; + pQueue->val = value; + pEvdev->num_queue++; +} + +/** + * Post button event right here, right now. + * Interface for MB emulation since these need to post immediately. + */ +void +EvdevPostButtonEvent(InputInfoPtr pInfo, int button, int value) +{ + xf86PostButtonEvent(pInfo->dev, 0, button, value, 0, 0); +} + +void +EvdevQueueButtonClicks(InputInfoPtr pInfo, int button, int count) +{ + int i; + + for (i = 0; i < count; i++) { + EvdevQueueButtonEvent(pInfo, button, 1); + EvdevQueueButtonEvent(pInfo, button, 0); + } +} + +/** + * Coming back from resume may leave us with a file descriptor that can be + * opened but fails on the first read (ENODEV). + * In this case, try to open the device until it becomes available or until + * the predefined count expires. + */ +static CARD32 +EvdevReopenTimer(OsTimerPtr timer, CARD32 time, pointer arg) +{ + InputInfoPtr pInfo = (InputInfoPtr)arg; + EvdevPtr pEvdev = pInfo->private; + + do { + pInfo->fd = open(pEvdev->device, O_RDWR | O_NONBLOCK, 0); + } while (pInfo->fd < 0 && errno == EINTR); + + if (pInfo->fd != -1) + { + if (EvdevCacheCompare(pInfo, TRUE) == Success) + { + xf86Msg(X_INFO, "%s: Device reopened after %d attempts.\n", pInfo->name, + pEvdev->reopen_attempts - pEvdev->reopen_left + 1); + EvdevOn(pInfo->dev); + } else + { + xf86Msg(X_ERROR, "%s: Device has changed - disabling.\n", + pInfo->name); + xf86DisableDevice(pInfo->dev, FALSE); + close(pInfo->fd); + pInfo->fd = -1; + pEvdev->min_maj = 0; /* don't hog the device */ + } + pEvdev->reopen_left = 0; + return 0; + } + + pEvdev->reopen_left--; + + if (!pEvdev->reopen_left) + { + xf86Msg(X_ERROR, "%s: Failed to reopen device after %d attempts.\n", + pInfo->name, pEvdev->reopen_attempts); + xf86DisableDevice(pInfo->dev, FALSE); + pEvdev->min_maj = 0; /* don't hog the device */ + return 0; + } + + return 100; /* come back in 100 ms */ +} + +#define ABS_X_VALUE 0x1 +#define ABS_Y_VALUE 0x2 +#define ABS_VALUE 0x4 +/** + * Take the valuators and process them accordingly. + */ +static void +EvdevProcessValuators(InputInfoPtr pInfo, int v[MAX_VALUATORS], int *num_v, + int *first_v) +{ + int tmp; + EvdevPtr pEvdev = pInfo->private; + + *num_v = *first_v = 0; + + /* convert to relative motion for touchpads */ + if (pEvdev->abs && (pEvdev->flags & EVDEV_TOUCHPAD)) { + if (pEvdev->tool) { /* meaning, touch is active */ + if (pEvdev->old_vals[0] != -1) + pEvdev->delta[REL_X] = pEvdev->vals[0] - pEvdev->old_vals[0]; + if (pEvdev->old_vals[1] != -1) + pEvdev->delta[REL_Y] = pEvdev->vals[1] - pEvdev->old_vals[1]; + if (pEvdev->abs & ABS_X_VALUE) + pEvdev->old_vals[0] = pEvdev->vals[0]; + if (pEvdev->abs & ABS_Y_VALUE) + pEvdev->old_vals[1] = pEvdev->vals[1]; + } else { + pEvdev->old_vals[0] = pEvdev->old_vals[1] = -1; + } + pEvdev->abs = 0; + pEvdev->rel = 1; + } + + if (pEvdev->rel) { + int first = REL_CNT, last = 0; + int i; + + if (pEvdev->swap_axes) { + tmp = pEvdev->delta[REL_X]; + pEvdev->delta[REL_X] = pEvdev->delta[REL_Y]; + pEvdev->delta[REL_Y] = tmp; + } + if (pEvdev->invert_x) + pEvdev->delta[REL_X] *= -1; + if (pEvdev->invert_y) + pEvdev->delta[REL_Y] *= -1; + + for (i = 0; i < REL_CNT; i++) + { + int map = pEvdev->axis_map[i]; + if (map != -1) + { + v[map] = pEvdev->delta[i]; + if (map < first) + first = map; + if (map > last) + last = map; + } + } + + *num_v = (last - first + 1); + *first_v = first; + } + /* + * Some devices only generate valid abs coords when BTN_DIGI is + * pressed. On wacom tablets, this means that the pen is in + * proximity of the tablet. After the pen is removed, BTN_DIGI is + * released, and a (0, 0) absolute event is generated. Checking + * pEvdev->digi here, lets us ignore that event. pEvdev is + * initialized to 1 so devices that doesn't use this scheme still + * just works. + */ + else if (pEvdev->abs && pEvdev->tool) { + memcpy(v, pEvdev->vals, sizeof(int) * pEvdev->num_vals); + + if (pEvdev->swap_axes) { + int tmp = v[0]; + v[0] = v[1]; + v[1] = tmp; + } + + if (pEvdev->flags & EVDEV_CALIBRATED) + { + v[0] = xf86ScaleAxis(v[0], + pEvdev->absinfo[ABS_X].maximum, + pEvdev->absinfo[ABS_X].minimum, + pEvdev->calibration.max_x, pEvdev->calibration.min_x); + v[1] = xf86ScaleAxis(v[1], + pEvdev->absinfo[ABS_Y].maximum, + pEvdev->absinfo[ABS_Y].minimum, + pEvdev->calibration.max_y, pEvdev->calibration.min_y); + } + + if (pEvdev->invert_x) + v[0] = (pEvdev->absinfo[ABS_X].maximum - v[0] + + pEvdev->absinfo[ABS_X].minimum); + if (pEvdev->invert_y) + v[1] = (pEvdev->absinfo[ABS_Y].maximum - v[1] + + pEvdev->absinfo[ABS_Y].minimum); + + *num_v = pEvdev->num_vals; + *first_v = 0; + } +} + +/** + * Take a button input event and process it accordingly. + */ +static void +EvdevProcessButtonEvent(InputInfoPtr pInfo, struct input_event *ev) +{ + unsigned int button; + int value; + EvdevPtr pEvdev = pInfo->private; + + button = EvdevUtilButtonEventToButtonNumber(pEvdev, ev->code); + + /* Get the signed value, earlier kernels had this as unsigned */ + value = ev->value; + + /* Handle drag lock */ + if (EvdevDragLockFilterEvent(pInfo, button, value)) + return; + + if (EvdevWheelEmuFilterButton(pInfo, button, value)) + return; + + if (EvdevMBEmuFilterEvent(pInfo, button, value)) + return; + + if (button) + EvdevQueueButtonEvent(pInfo, button, value); + else + EvdevQueueKbdEvent(pInfo, ev, value); +} + +/** + * Take the relative motion input event and process it accordingly. + */ +static void +EvdevProcessRelativeMotionEvent(InputInfoPtr pInfo, struct input_event *ev) +{ + static int value; + EvdevPtr pEvdev = pInfo->private; + + /* Get the signed value, earlier kernels had this as unsigned */ + value = ev->value; + + pEvdev->rel = 1; + + switch (ev->code) { + case REL_WHEEL: + if (value > 0) + EvdevQueueButtonClicks(pInfo, wheel_up_button, value); + else if (value < 0) + EvdevQueueButtonClicks(pInfo, wheel_down_button, -value); + break; + + case REL_DIAL: + case REL_HWHEEL: + if (value > 0) + EvdevQueueButtonClicks(pInfo, wheel_right_button, value); + else if (value < 0) + EvdevQueueButtonClicks(pInfo, wheel_left_button, -value); + break; + + /* We don't post wheel events as axis motion. */ + default: + /* Ignore EV_REL events if we never set up for them. */ + if (!(pEvdev->flags & EVDEV_RELATIVE_EVENTS)) + return; + + /* Handle mouse wheel emulation */ + if (EvdevWheelEmuFilterMotion(pInfo, ev)) + return; + + pEvdev->delta[ev->code] += value; + break; + } +} + +/** + * Take the absolute motion input event and process it accordingly. + */ +static void +EvdevProcessAbsoluteMotionEvent(InputInfoPtr pInfo, struct input_event *ev) +{ + static int value; + EvdevPtr pEvdev = pInfo->private; + + /* Get the signed value, earlier kernels had this as unsigned */ + value = ev->value; + + /* Ignore EV_ABS events if we never set up for them. */ + if (!(pEvdev->flags & EVDEV_ABSOLUTE_EVENTS)) + return; + + if (ev->code > ABS_MAX) + return; + + pEvdev->vals[pEvdev->axis_map[ev->code]] = value; + if (ev->code == ABS_X) + pEvdev->abs |= ABS_X_VALUE; + else if (ev->code == ABS_Y) + pEvdev->abs |= ABS_Y_VALUE; + else + pEvdev->abs |= ABS_VALUE; +} + +/** + * Take the key press/release input event and process it accordingly. + */ +static void +EvdevProcessKeyEvent(InputInfoPtr pInfo, struct input_event *ev) +{ + static int value; + EvdevPtr pEvdev = pInfo->private; + + /* Get the signed value, earlier kernels had this as unsigned */ + value = ev->value; + + /* don't repeat mouse buttons */ + if (ev->code >= BTN_MOUSE && ev->code < KEY_OK) + if (value == 2) + return; + + switch (ev->code) { + case BTN_TOOL_PEN: + case BTN_TOOL_RUBBER: + case BTN_TOOL_BRUSH: + case BTN_TOOL_PENCIL: + case BTN_TOOL_AIRBRUSH: + case BTN_TOOL_FINGER: + case BTN_TOOL_MOUSE: + case BTN_TOOL_LENS: + pEvdev->tool = value ? ev->code : 0; + break; + + case BTN_TOUCH: + pEvdev->tool = value ? ev->code : 0; + if (!(pEvdev->flags & (EVDEV_TOUCHSCREEN | EVDEV_TABLET))) + break; + /* Treat BTN_TOUCH from devices that only have BTN_TOUCH as + * BTN_LEFT. */ + ev->code = BTN_LEFT; + /* Intentional fallthrough! */ + + default: + EvdevProcessButtonEvent(pInfo, ev); + break; + } +} + +/** + * Post the relative motion events. + */ +void +EvdevPostRelativeMotionEvents(InputInfoPtr pInfo, int *num_v, int *first_v, + int v[MAX_VALUATORS]) +{ + EvdevPtr pEvdev = pInfo->private; + + if (pEvdev->rel) { + xf86PostMotionEventP(pInfo->dev, FALSE, *first_v, *num_v, v + *first_v); + } +} + +/** + * Post the absolute motion events. + */ +void +EvdevPostAbsoluteMotionEvents(InputInfoPtr pInfo, int *num_v, int *first_v, + int v[MAX_VALUATORS]) +{ + EvdevPtr pEvdev = pInfo->private; + + /* + * Some devices only generate valid abs coords when BTN_DIGI is + * pressed. On wacom tablets, this means that the pen is in + * proximity of the tablet. After the pen is removed, BTN_DIGI is + * released, and a (0, 0) absolute event is generated. Checking + * pEvdev->digi here, lets us ignore that event. pEvdev is + * initialized to 1 so devices that doesn't use this scheme still + * just works. + */ + if (pEvdev->abs && pEvdev->tool) { + xf86PostMotionEventP(pInfo->dev, TRUE, *first_v, *num_v, v); + } +} + +/** + * Post the queued key/button events. + */ +static void EvdevPostQueuedEvents(InputInfoPtr pInfo, int *num_v, int *first_v, + int v[MAX_VALUATORS]) +{ + int i; + EvdevPtr pEvdev = pInfo->private; + + for (i = 0; i < pEvdev->num_queue; i++) { + switch (pEvdev->queue[i].type) { + case EV_QUEUE_KEY: + xf86PostKeyboardEvent(pInfo->dev, pEvdev->queue[i].key, + pEvdev->queue[i].val); + break; + case EV_QUEUE_BTN: + /* FIXME: Add xf86PostButtonEventP to the X server so that we may + * pass the valuators on ButtonPress/Release events, too. Currently + * only MotionNotify events contain the pointer position. */ + xf86PostButtonEvent(pInfo->dev, 0, pEvdev->queue[i].key, + pEvdev->queue[i].val, 0, 0); + break; + } + } +} + +/** + * Take the synchronization input event and process it accordingly; the motion + * notify events are sent first, then any button/key press/release events. + */ +static void +EvdevProcessSyncEvent(InputInfoPtr pInfo, struct input_event *ev) +{ + int num_v = 0, first_v = 0; + int v[MAX_VALUATORS]; + EvdevPtr pEvdev = pInfo->private; + + EvdevProcessValuators(pInfo, v, &num_v, &first_v); + + EvdevPostRelativeMotionEvents(pInfo, &num_v, &first_v, v); + EvdevPostAbsoluteMotionEvents(pInfo, &num_v, &first_v, v); + EvdevPostQueuedEvents(pInfo, &num_v, &first_v, v); + + memset(pEvdev->delta, 0, sizeof(pEvdev->delta)); + memset(pEvdev->queue, 0, sizeof(pEvdev->queue)); + pEvdev->num_queue = 0; + pEvdev->abs = 0; + pEvdev->rel = 0; +} + +/** + * Process the events from the device; nothing is actually posted to the server + * until an EV_SYN event is received. + */ +static void +EvdevProcessEvent(InputInfoPtr pInfo, struct input_event *ev) +{ + switch (ev->type) { + case EV_REL: + EvdevProcessRelativeMotionEvent(pInfo, ev); + break; + case EV_ABS: + EvdevProcessAbsoluteMotionEvent(pInfo, ev); + break; + case EV_KEY: + EvdevProcessKeyEvent(pInfo, ev); + break; + case EV_SYN: + EvdevProcessSyncEvent(pInfo, ev); + break; + } +} + +#undef ABS_X_VALUE +#undef ABS_Y_VALUE +#undef ABS_VALUE + +/* just a magic number to reduce the number of reads */ +#define NUM_EVENTS 16 + +static void +EvdevReadInput(InputInfoPtr pInfo) +{ + struct input_event ev[NUM_EVENTS]; + int i, len = sizeof(ev); + EvdevPtr pEvdev = pInfo->private; + + while (len == sizeof(ev)) + { + len = read(pInfo->fd, &ev, sizeof(ev)); + if (len <= 0) + { + if (errno == ENODEV) /* May happen after resume */ + { + EvdevMBEmuFinalize(pInfo); + xf86RemoveEnabledDevice(pInfo); + close(pInfo->fd); + pInfo->fd = -1; + if (pEvdev->reopen_timer) + { + pEvdev->reopen_left = pEvdev->reopen_attempts; + pEvdev->reopen_timer = TimerSet(pEvdev->reopen_timer, 0, 100, EvdevReopenTimer, pInfo); + } + } else if (errno != EAGAIN) + { + /* We use X_NONE here because it doesn't alloc */ + xf86MsgVerb(X_NONE, 0, "%s: Read error: %s\n", pInfo->name, + strerror(errno)); + } + break; + } + + /* The kernel promises that we always only read a complete + * event, so len != sizeof ev is an error. */ + if (len % sizeof(ev[0])) { + /* We use X_NONE here because it doesn't alloc */ + xf86MsgVerb(X_NONE, 0, "%s: Read error: %s\n", pInfo->name, strerror(errno)); + break; + } + + for (i = 0; i < len/sizeof(ev[0]); i++) + EvdevProcessEvent(pInfo, &ev[i]); + } +} + +#define TestBit(bit, array) ((array[(bit) / LONG_BITS]) & (1L << ((bit) % LONG_BITS))) + +static void +EvdevPtrCtrlProc(DeviceIntPtr device, PtrCtrl *ctrl) +{ + /* Nothing to do, dix handles all settings */ +} + +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5 +static KeySym map[] = { + /* 0x00 */ NoSymbol, NoSymbol, + /* 0x01 */ XK_Escape, NoSymbol, + /* 0x02 */ XK_1, XK_exclam, + /* 0x03 */ XK_2, XK_at, + /* 0x04 */ XK_3, XK_numbersign, + /* 0x05 */ XK_4, XK_dollar, + /* 0x06 */ XK_5, XK_percent, + /* 0x07 */ XK_6, XK_asciicircum, + /* 0x08 */ XK_7, XK_ampersand, + /* 0x09 */ XK_8, XK_asterisk, + /* 0x0a */ XK_9, XK_parenleft, + /* 0x0b */ XK_0, XK_parenright, + /* 0x0c */ XK_minus, XK_underscore, + /* 0x0d */ XK_equal, XK_plus, + /* 0x0e */ XK_BackSpace, NoSymbol, + /* 0x0f */ XK_Tab, XK_ISO_Left_Tab, + /* 0x10 */ XK_Q, NoSymbol, + /* 0x11 */ XK_W, NoSymbol, + /* 0x12 */ XK_E, NoSymbol, + /* 0x13 */ XK_R, NoSymbol, + /* 0x14 */ XK_T, NoSymbol, + /* 0x15 */ XK_Y, NoSymbol, + /* 0x16 */ XK_U, NoSymbol, + /* 0x17 */ XK_I, NoSymbol, + /* 0x18 */ XK_O, NoSymbol, + /* 0x19 */ XK_P, NoSymbol, + /* 0x1a */ XK_bracketleft, XK_braceleft, + /* 0x1b */ XK_bracketright,XK_braceright, + /* 0x1c */ XK_Return, NoSymbol, + /* 0x1d */ XK_Control_L, NoSymbol, + /* 0x1e */ XK_A, NoSymbol, + /* 0x1f */ XK_S, NoSymbol, + /* 0x20 */ XK_D, NoSymbol, + /* 0x21 */ XK_F, NoSymbol, + /* 0x22 */ XK_G, NoSymbol, + /* 0x23 */ XK_H, NoSymbol, + /* 0x24 */ XK_J, NoSymbol, + /* 0x25 */ XK_K, NoSymbol, + /* 0x26 */ XK_L, NoSymbol, + /* 0x27 */ XK_semicolon, XK_colon, + /* 0x28 */ XK_quoteright, XK_quotedbl, + /* 0x29 */ XK_quoteleft, XK_asciitilde, + /* 0x2a */ XK_Shift_L, NoSymbol, + /* 0x2b */ XK_backslash, XK_bar, + /* 0x2c */ XK_Z, NoSymbol, + /* 0x2d */ XK_X, NoSymbol, + /* 0x2e */ XK_C, NoSymbol, + /* 0x2f */ XK_V, NoSymbol, + /* 0x30 */ XK_B, NoSymbol, + /* 0x31 */ XK_N, NoSymbol, + /* 0x32 */ XK_M, NoSymbol, + /* 0x33 */ XK_comma, XK_less, + /* 0x34 */ XK_period, XK_greater, + /* 0x35 */ XK_slash, XK_question, + /* 0x36 */ XK_Shift_R, NoSymbol, + /* 0x37 */ XK_KP_Multiply, NoSymbol, + /* 0x38 */ XK_Alt_L, XK_Meta_L, + /* 0x39 */ XK_space, NoSymbol, + /* 0x3a */ XK_Caps_Lock, NoSymbol, + /* 0x3b */ XK_F1, NoSymbol, + /* 0x3c */ XK_F2, NoSymbol, + /* 0x3d */ XK_F3, NoSymbol, + /* 0x3e */ XK_F4, NoSymbol, + /* 0x3f */ XK_F5, NoSymbol, + /* 0x40 */ XK_F6, NoSymbol, + /* 0x41 */ XK_F7, NoSymbol, + /* 0x42 */ XK_F8, NoSymbol, + /* 0x43 */ XK_F9, NoSymbol, + /* 0x44 */ XK_F10, NoSymbol, + /* 0x45 */ XK_Num_Lock, NoSymbol, + /* 0x46 */ XK_Scroll_Lock, NoSymbol, + /* These KP keys should have the KP_7 keysyms in the numlock + * modifer... ? */ + /* 0x47 */ XK_KP_Home, XK_KP_7, + /* 0x48 */ XK_KP_Up, XK_KP_8, + /* 0x49 */ XK_KP_Prior, XK_KP_9, + /* 0x4a */ XK_KP_Subtract, NoSymbol, + /* 0x4b */ XK_KP_Left, XK_KP_4, + /* 0x4c */ XK_KP_Begin, XK_KP_5, + /* 0x4d */ XK_KP_Right, XK_KP_6, + /* 0x4e */ XK_KP_Add, NoSymbol, + /* 0x4f */ XK_KP_End, XK_KP_1, + /* 0x50 */ XK_KP_Down, XK_KP_2, + /* 0x51 */ XK_KP_Next, XK_KP_3, + /* 0x52 */ XK_KP_Insert, XK_KP_0, + /* 0x53 */ XK_KP_Delete, XK_KP_Decimal, + /* 0x54 */ NoSymbol, NoSymbol, + /* 0x55 */ XK_F13, NoSymbol, + /* 0x56 */ XK_less, XK_greater, + /* 0x57 */ XK_F11, NoSymbol, + /* 0x58 */ XK_F12, NoSymbol, + /* 0x59 */ XK_F14, NoSymbol, + /* 0x5a */ XK_F15, NoSymbol, + /* 0x5b */ XK_F16, NoSymbol, + /* 0x5c */ XK_F17, NoSymbol, + /* 0x5d */ XK_F18, NoSymbol, + /* 0x5e */ XK_F19, NoSymbol, + /* 0x5f */ XK_F20, NoSymbol, + /* 0x60 */ XK_KP_Enter, NoSymbol, + /* 0x61 */ XK_Control_R, NoSymbol, + /* 0x62 */ XK_KP_Divide, NoSymbol, + /* 0x63 */ XK_Print, XK_Sys_Req, + /* 0x64 */ XK_Alt_R, XK_Meta_R, + /* 0x65 */ NoSymbol, NoSymbol, /* KEY_LINEFEED */ + /* 0x66 */ XK_Home, NoSymbol, + /* 0x67 */ XK_Up, NoSymbol, + /* 0x68 */ XK_Prior, NoSymbol, + /* 0x69 */ XK_Left, NoSymbol, + /* 0x6a */ XK_Right, NoSymbol, + /* 0x6b */ XK_End, NoSymbol, + /* 0x6c */ XK_Down, NoSymbol, + /* 0x6d */ XK_Next, NoSymbol, + /* 0x6e */ XK_Insert, NoSymbol, + /* 0x6f */ XK_Delete, NoSymbol, + /* 0x70 */ NoSymbol, NoSymbol, /* KEY_MACRO */ + /* 0x71 */ NoSymbol, NoSymbol, + /* 0x72 */ NoSymbol, NoSymbol, + /* 0x73 */ NoSymbol, NoSymbol, + /* 0x74 */ NoSymbol, NoSymbol, + /* 0x75 */ XK_KP_Equal, NoSymbol, + /* 0x76 */ NoSymbol, NoSymbol, + /* 0x77 */ NoSymbol, NoSymbol, + /* 0x78 */ XK_F21, NoSymbol, + /* 0x79 */ XK_F22, NoSymbol, + /* 0x7a */ XK_F23, NoSymbol, + /* 0x7b */ XK_F24, NoSymbol, + /* 0x7c */ XK_KP_Separator, NoSymbol, + /* 0x7d */ XK_Meta_L, NoSymbol, + /* 0x7e */ XK_Meta_R, NoSymbol, + /* 0x7f */ XK_Multi_key, NoSymbol, + /* 0x80 */ NoSymbol, NoSymbol, + /* 0x81 */ NoSymbol, NoSymbol, + /* 0x82 */ NoSymbol, NoSymbol, + /* 0x83 */ NoSymbol, NoSymbol, + /* 0x84 */ NoSymbol, NoSymbol, + /* 0x85 */ NoSymbol, NoSymbol, + /* 0x86 */ NoSymbol, NoSymbol, + /* 0x87 */ NoSymbol, NoSymbol, + /* 0x88 */ NoSymbol, NoSymbol, + /* 0x89 */ NoSymbol, NoSymbol, + /* 0x8a */ NoSymbol, NoSymbol, + /* 0x8b */ NoSymbol, NoSymbol, + /* 0x8c */ NoSymbol, NoSymbol, + /* 0x8d */ NoSymbol, NoSymbol, + /* 0x8e */ NoSymbol, NoSymbol, + /* 0x8f */ NoSymbol, NoSymbol, + /* 0x90 */ NoSymbol, NoSymbol, + /* 0x91 */ NoSymbol, NoSymbol, + /* 0x92 */ NoSymbol, NoSymbol, + /* 0x93 */ NoSymbol, NoSymbol, + /* 0x94 */ NoSymbol, NoSymbol, + /* 0x95 */ NoSymbol, NoSymbol, + /* 0x96 */ NoSymbol, NoSymbol, + /* 0x97 */ NoSymbol, NoSymbol, + /* 0x98 */ NoSymbol, NoSymbol, + /* 0x99 */ NoSymbol, NoSymbol, + /* 0x9a */ NoSymbol, NoSymbol, + /* 0x9b */ NoSymbol, NoSymbol, + /* 0x9c */ NoSymbol, NoSymbol, + /* 0x9d */ NoSymbol, NoSymbol, + /* 0x9e */ NoSymbol, NoSymbol, + /* 0x9f */ NoSymbol, NoSymbol, + /* 0xa0 */ NoSymbol, NoSymbol, + /* 0xa1 */ NoSymbol, NoSymbol, + /* 0xa2 */ NoSymbol, NoSymbol, + /* 0xa3 */ NoSymbol, NoSymbol, + /* 0xa4 */ NoSymbol, NoSymbol, + /* 0xa5 */ NoSymbol, NoSymbol, + /* 0xa6 */ NoSymbol, NoSymbol, + /* 0xa7 */ NoSymbol, NoSymbol, + /* 0xa8 */ NoSymbol, NoSymbol, + /* 0xa9 */ NoSymbol, NoSymbol, + /* 0xaa */ NoSymbol, NoSymbol, + /* 0xab */ NoSymbol, NoSymbol, + /* 0xac */ NoSymbol, NoSymbol, + /* 0xad */ NoSymbol, NoSymbol, + /* 0xae */ NoSymbol, NoSymbol, + /* 0xaf */ NoSymbol, NoSymbol, + /* 0xb0 */ NoSymbol, NoSymbol, + /* 0xb1 */ NoSymbol, NoSymbol, + /* 0xb2 */ NoSymbol, NoSymbol, + /* 0xb3 */ NoSymbol, NoSymbol, + /* 0xb4 */ NoSymbol, NoSymbol, + /* 0xb5 */ NoSymbol, NoSymbol, + /* 0xb6 */ NoSymbol, NoSymbol, + /* 0xb7 */ NoSymbol, NoSymbol, + /* 0xb8 */ NoSymbol, NoSymbol, + /* 0xb9 */ NoSymbol, NoSymbol, + /* 0xba */ NoSymbol, NoSymbol, + /* 0xbb */ NoSymbol, NoSymbol, + /* 0xbc */ NoSymbol, NoSymbol, + /* 0xbd */ NoSymbol, NoSymbol, + /* 0xbe */ NoSymbol, NoSymbol, + /* 0xbf */ NoSymbol, NoSymbol, + /* 0xc0 */ NoSymbol, NoSymbol, + /* 0xc1 */ NoSymbol, NoSymbol, + /* 0xc2 */ NoSymbol, NoSymbol, + /* 0xc3 */ NoSymbol, NoSymbol, + /* 0xc4 */ NoSymbol, NoSymbol, + /* 0xc5 */ NoSymbol, NoSymbol, + /* 0xc6 */ NoSymbol, NoSymbol, + /* 0xc7 */ NoSymbol, NoSymbol, + /* 0xc8 */ NoSymbol, NoSymbol, + /* 0xc9 */ NoSymbol, NoSymbol, + /* 0xca */ NoSymbol, NoSymbol, + /* 0xcb */ NoSymbol, NoSymbol, + /* 0xcc */ NoSymbol, NoSymbol, + /* 0xcd */ NoSymbol, NoSymbol, + /* 0xce */ NoSymbol, NoSymbol, + /* 0xcf */ NoSymbol, NoSymbol, + /* 0xd0 */ NoSymbol, NoSymbol, + /* 0xd1 */ NoSymbol, NoSymbol, + /* 0xd2 */ NoSymbol, NoSymbol, + /* 0xd3 */ NoSymbol, NoSymbol, + /* 0xd4 */ NoSymbol, NoSymbol, + /* 0xd5 */ NoSymbol, NoSymbol, + /* 0xd6 */ NoSymbol, NoSymbol, + /* 0xd7 */ NoSymbol, NoSymbol, + /* 0xd8 */ NoSymbol, NoSymbol, + /* 0xd9 */ NoSymbol, NoSymbol, + /* 0xda */ NoSymbol, NoSymbol, + /* 0xdb */ NoSymbol, NoSymbol, + /* 0xdc */ NoSymbol, NoSymbol, + /* 0xdd */ NoSymbol, NoSymbol, + /* 0xde */ NoSymbol, NoSymbol, + /* 0xdf */ NoSymbol, NoSymbol, + /* 0xe0 */ NoSymbol, NoSymbol, + /* 0xe1 */ NoSymbol, NoSymbol, + /* 0xe2 */ NoSymbol, NoSymbol, + /* 0xe3 */ NoSymbol, NoSymbol, + /* 0xe4 */ NoSymbol, NoSymbol, + /* 0xe5 */ NoSymbol, NoSymbol, + /* 0xe6 */ NoSymbol, NoSymbol, + /* 0xe7 */ NoSymbol, NoSymbol, + /* 0xe8 */ NoSymbol, NoSymbol, + /* 0xe9 */ NoSymbol, NoSymbol, + /* 0xea */ NoSymbol, NoSymbol, + /* 0xeb */ NoSymbol, NoSymbol, + /* 0xec */ NoSymbol, NoSymbol, + /* 0xed */ NoSymbol, NoSymbol, + /* 0xee */ NoSymbol, NoSymbol, + /* 0xef */ NoSymbol, NoSymbol, + /* 0xf0 */ NoSymbol, NoSymbol, + /* 0xf1 */ NoSymbol, NoSymbol, + /* 0xf2 */ NoSymbol, NoSymbol, + /* 0xf3 */ NoSymbol, NoSymbol, + /* 0xf4 */ NoSymbol, NoSymbol, + /* 0xf5 */ NoSymbol, NoSymbol, + /* 0xf6 */ NoSymbol, NoSymbol, + /* 0xf7 */ NoSymbol, NoSymbol, +}; + +static struct { KeySym keysym; CARD8 mask; } modifiers[] = { + { XK_Shift_L, ShiftMask }, + { XK_Shift_R, ShiftMask }, + { XK_Control_L, ControlMask }, + { XK_Control_R, ControlMask }, + { XK_Caps_Lock, LockMask }, + { XK_Alt_L, AltMask }, + { XK_Alt_R, AltMask }, + { XK_Meta_L, Mod4Mask }, + { XK_Meta_R, Mod4Mask }, + { XK_Num_Lock, NumLockMask }, + { XK_Scroll_Lock, ScrollLockMask }, + { XK_Mode_switch, AltLangMask } +}; + +/* Server 1.6 and earlier */ +static int +EvdevInitKeysyms(DeviceIntPtr device) +{ + InputInfoPtr pInfo; + EvdevPtr pEvdev; + KeySymsRec keySyms; + CARD8 modMap[MAP_LENGTH]; + KeySym sym; + int i, j; + + pInfo = device->public.devicePrivate; + pEvdev = pInfo->private; + + /* Compute the modifier map */ + memset(modMap, 0, sizeof modMap); + + for (i = 0; i < ArrayLength(map) / GLYPHS_PER_KEY; i++) { + sym = map[i * GLYPHS_PER_KEY]; + for (j = 0; j < ArrayLength(modifiers); j++) { + if (modifiers[j].keysym == sym) + modMap[i + MIN_KEYCODE] = modifiers[j].mask; + } + } + + keySyms.map = map; + keySyms.mapWidth = GLYPHS_PER_KEY; + keySyms.minKeyCode = MIN_KEYCODE; + keySyms.maxKeyCode = MIN_KEYCODE + ArrayLength(map) / GLYPHS_PER_KEY - 1; + + XkbSetRulesDflts(pEvdev->rmlvo.rules, pEvdev->rmlvo.model, + pEvdev->rmlvo.layout, pEvdev->rmlvo.variant, + pEvdev->rmlvo.options); + if (!XkbInitKeyboardDeviceStruct(device, &pEvdev->xkbnames, + &keySyms, modMap, NULL, + EvdevKbdCtrl)) + return 0; + + return 1; +} +#endif + +static void +EvdevKbdCtrl(DeviceIntPtr device, KeybdCtrl *ctrl) +{ + static struct { int xbit, code; } bits[] = { + { CAPSFLAG, LED_CAPSL }, + { NUMFLAG, LED_NUML }, + { SCROLLFLAG, LED_SCROLLL }, + { MODEFLAG, LED_KANA }, + { COMPOSEFLAG, LED_COMPOSE } + }; + + InputInfoPtr pInfo; + struct input_event ev[ArrayLength(bits)]; + int i; + + memset(ev, 0, sizeof(ev)); + + pInfo = device->public.devicePrivate; + for (i = 0; i < ArrayLength(bits); i++) { + ev[i].type = EV_LED; + ev[i].code = bits[i].code; + ev[i].value = (ctrl->leds & bits[i].xbit) > 0; + } + + write(pInfo->fd, ev, sizeof ev); +} + +static int +EvdevAddKeyClass(DeviceIntPtr device) +{ + InputInfoPtr pInfo; + EvdevPtr pEvdev; + + pInfo = device->public.devicePrivate; + pEvdev = pInfo->private; + + /* sorry, no rules change allowed for you */ + xf86ReplaceStrOption(pInfo->options, "xkb_rules", "evdev"); + SetXkbOption(pInfo, "xkb_rules", &pEvdev->rmlvo.rules); + SetXkbOption(pInfo, "xkb_model", &pEvdev->rmlvo.model); + if (!pEvdev->rmlvo.model) + SetXkbOption(pInfo, "XkbModel", &pEvdev->rmlvo.model); + SetXkbOption(pInfo, "xkb_layout", &pEvdev->rmlvo.layout); + if (!pEvdev->rmlvo.layout) + SetXkbOption(pInfo, "XkbLayout", &pEvdev->rmlvo.layout); + SetXkbOption(pInfo, "xkb_variant", &pEvdev->rmlvo.variant); + if (!pEvdev->rmlvo.variant) + SetXkbOption(pInfo, "XkbVariant", &pEvdev->rmlvo.variant); + SetXkbOption(pInfo, "xkb_options", &pEvdev->rmlvo.options); + if (!pEvdev->rmlvo.options) + SetXkbOption(pInfo, "XkbOptions", &pEvdev->rmlvo.options); + +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 5 + if (!InitKeyboardDeviceStruct(device, &pEvdev->rmlvo, NULL, EvdevKbdCtrl)) + return !Success; +#else + if (!EvdevInitKeysyms(device)) + return !Success; + +#endif + + pInfo->flags |= XI86_KEYBOARD_CAPABLE; + + return Success; +} + +static int +EvdevAddAbsClass(DeviceIntPtr device) +{ + InputInfoPtr pInfo; + EvdevPtr pEvdev; + int num_axes, axis, i = 0; + Atom *atoms; + + pInfo = device->public.devicePrivate; + pEvdev = pInfo->private; + + if (!TestBit(EV_ABS, pEvdev->bitmask)) + return !Success; + + num_axes = CountBits(pEvdev->abs_bitmask, NLONGS(ABS_MAX)); + if (num_axes < 1) + return !Success; + pEvdev->num_vals = num_axes; + memset(pEvdev->vals, 0, num_axes * sizeof(int)); + memset(pEvdev->old_vals, -1, num_axes * sizeof(int)); + atoms = malloc(pEvdev->num_vals * sizeof(Atom)); + + for (axis = ABS_X; axis <= ABS_MAX; axis++) { + pEvdev->axis_map[axis] = -1; + if (!TestBit(axis, pEvdev->abs_bitmask)) + continue; + pEvdev->axis_map[axis] = i; + i++; + } + + EvdevInitAxesLabels(pEvdev, pEvdev->num_vals, atoms); + + if (!InitValuatorClassDeviceStruct(device, num_axes, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + atoms, +#endif +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3 + GetMotionHistory, +#endif + GetMotionHistorySize(), Absolute)) + return !Success; + + for (axis = ABS_X; axis <= ABS_MAX; axis++) { + int axnum = pEvdev->axis_map[axis]; + if (axnum == -1) + continue; + xf86InitValuatorAxisStruct(device, axnum, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + atoms[axnum], +#endif + pEvdev->absinfo[axis].minimum, + pEvdev->absinfo[axis].maximum, + 10000, 0, 10000); + xf86InitValuatorDefaults(device, axnum); + pEvdev->old_vals[axnum] = -1; + } + + free(atoms); + + if (!InitPtrFeedbackClassDeviceStruct(device, EvdevPtrCtrlProc)) + return !Success; + + if ((TestBit(ABS_X, pEvdev->abs_bitmask) && + TestBit(ABS_Y, pEvdev->abs_bitmask)) || + (TestBit(ABS_RX, pEvdev->abs_bitmask) && + TestBit(ABS_RY, pEvdev->abs_bitmask)) || + (TestBit(ABS_HAT0X, pEvdev->abs_bitmask) && + TestBit(ABS_HAT0Y, pEvdev->abs_bitmask)) || + (TestBit(ABS_HAT1X, pEvdev->abs_bitmask) && + TestBit(ABS_HAT1Y, pEvdev->abs_bitmask)) || + (TestBit(ABS_HAT2X, pEvdev->abs_bitmask) && + TestBit(ABS_HAT2Y, pEvdev->abs_bitmask)) || + (TestBit(ABS_HAT3X, pEvdev->abs_bitmask) && + TestBit(ABS_HAT3Y, pEvdev->abs_bitmask)) || + (TestBit(ABS_TILT_X, pEvdev->abs_bitmask) && + TestBit(ABS_TILT_Y, pEvdev->abs_bitmask))) + pInfo->flags |= XI86_POINTER_CAPABLE; + + return Success; +} + +static int +EvdevAddRelClass(DeviceIntPtr device) +{ + InputInfoPtr pInfo; + EvdevPtr pEvdev; + int num_axes, axis, i = 0; + Atom *atoms; + + pInfo = device->public.devicePrivate; + pEvdev = pInfo->private; + + if (!TestBit(EV_REL, pEvdev->bitmask)) + return !Success; + + num_axes = CountBits(pEvdev->rel_bitmask, NLONGS(REL_MAX)); + if (num_axes < 1) + return !Success; + + /* Wheels are special, we post them as button events. So let's ignore them + * in the axes list too */ + if (TestBit(REL_WHEEL, pEvdev->rel_bitmask)) + num_axes--; + if (TestBit(REL_HWHEEL, pEvdev->rel_bitmask)) + num_axes--; + if (TestBit(REL_DIAL, pEvdev->rel_bitmask)) + num_axes--; + + if (num_axes <= 0) + return !Success; + + pEvdev->num_vals = num_axes; + memset(pEvdev->vals, 0, num_axes * sizeof(int)); + atoms = malloc(pEvdev->num_vals * sizeof(Atom)); + + for (axis = REL_X; axis <= REL_MAX; axis++) + { + pEvdev->axis_map[axis] = -1; + /* We don't post wheel events, so ignore them here too */ + if (axis == REL_WHEEL || axis == REL_HWHEEL || axis == REL_DIAL) + continue; + if (!TestBit(axis, pEvdev->rel_bitmask)) + continue; + pEvdev->axis_map[axis] = i; + i++; + } + + EvdevInitAxesLabels(pEvdev, pEvdev->num_vals, atoms); + + if (!InitValuatorClassDeviceStruct(device, num_axes, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + atoms, +#endif +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3 + GetMotionHistory, +#endif + GetMotionHistorySize(), Relative)) + return !Success; + + for (axis = REL_X; axis <= REL_MAX; axis++) + { + int axnum = pEvdev->axis_map[axis]; + + if (axnum == -1) + continue; + xf86InitValuatorAxisStruct(device, axnum, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + atoms[axnum], +#endif + -1, -1, 1, 0, 1); + xf86InitValuatorDefaults(device, axnum); + } + + free(atoms); + + if (!InitPtrFeedbackClassDeviceStruct(device, EvdevPtrCtrlProc)) + return !Success; + + pInfo->flags |= XI86_POINTER_CAPABLE; + + return Success; +} + +static int +EvdevAddButtonClass(DeviceIntPtr device) +{ + InputInfoPtr pInfo; + EvdevPtr pEvdev; + Atom *labels; + + pInfo = device->public.devicePrivate; + pEvdev = pInfo->private; + + labels = malloc(pEvdev->num_buttons * sizeof(Atom)); + EvdevInitButtonLabels(pEvdev, pEvdev->num_buttons, labels); + + if (!InitButtonClassDeviceStruct(device, pEvdev->num_buttons, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + labels, +#endif + pEvdev->btnmap)) + return !Success; + + free(labels); + return Success; +} + +/** + * Init the button mapping for the device. By default, this is a 1:1 mapping, + * i.e. Button 1 maps to Button 1, Button 2 to 2, etc. + * + * If a mapping has been specified, the mapping is the default, with the + * user-defined ones overwriting the defaults. + * i.e. a user-defined mapping of "3 2 1" results in a mapping of 3 2 1 4 5 6 ... + * + * Invalid button mappings revert to the default. + * + * Note that index 0 is unused, button 0 does not exist. + * This mapping is initialised for all devices, but only applied if the device + * has buttons (in EvdevAddButtonClass). + */ +static void +EvdevInitButtonMapping(InputInfoPtr pInfo) +{ + int i, nbuttons = 1; + char *mapping = NULL; + EvdevPtr pEvdev = pInfo->private; + + /* Check for user-defined button mapping */ + if ((mapping = xf86CheckStrOption(pInfo->options, "ButtonMapping", NULL))) + { + char *s = " "; + int btn = 0; + + xf86Msg(X_CONFIG, "%s: ButtonMapping '%s'\n", pInfo->name, mapping); + while (s && *s != '\0' && nbuttons < EVDEV_MAXBUTTONS) + { + btn = strtol(mapping, &s, 10); + + if (s == mapping || btn < 0 || btn > EVDEV_MAXBUTTONS) + { + xf86Msg(X_ERROR, + "%s: ... Invalid button mapping. Using defaults\n", + pInfo->name); + nbuttons = 1; /* ensure defaults start at 1 */ + break; + } + + pEvdev->btnmap[nbuttons++] = btn; + mapping = s; + } + } + + for (i = nbuttons; i < ArrayLength(pEvdev->btnmap); i++) + pEvdev->btnmap[i] = i; + +} + +static void +EvdevInitAnyClass(DeviceIntPtr device, EvdevPtr pEvdev) +{ + if (pEvdev->flags & EVDEV_RELATIVE_EVENTS && + EvdevAddRelClass(device) == Success) + xf86Msg(X_INFO, "%s: initialized for relative axes.\n", device->name); + if (pEvdev->flags & EVDEV_ABSOLUTE_EVENTS && + EvdevAddAbsClass(device) == Success) + xf86Msg(X_INFO, "%s: initialized for absolute axes.\n", device->name); +} + +static void +EvdevInitAbsClass(DeviceIntPtr device, EvdevPtr pEvdev) +{ + if (EvdevAddAbsClass(device) == Success) { + + xf86Msg(X_INFO,"%s: initialized for absolute axes.\n", device->name); + + } else { + + xf86Msg(X_ERROR,"%s: failed to initialize for absolute axes.\n", + device->name); + + pEvdev->flags &= ~EVDEV_ABSOLUTE_EVENTS; + + } +} + +static void +EvdevInitRelClass(DeviceIntPtr device, EvdevPtr pEvdev) +{ + int has_abs_axes = pEvdev->flags & EVDEV_ABSOLUTE_EVENTS; + + if (EvdevAddRelClass(device) == Success) { + + xf86Msg(X_INFO,"%s: initialized for relative axes.\n", device->name); + + if (has_abs_axes) { + + xf86Msg(X_WARNING,"%s: ignoring absolute axes.\n", device->name); + pEvdev->flags &= ~EVDEV_ABSOLUTE_EVENTS; + } + + } else { + + xf86Msg(X_ERROR,"%s: failed to initialize for relative axes.\n", + device->name); + + pEvdev->flags &= ~EVDEV_RELATIVE_EVENTS; + + if (has_abs_axes) + EvdevInitAbsClass(device, pEvdev); + } +} + +static void +EvdevInitTouchDevice(DeviceIntPtr device, EvdevPtr pEvdev) +{ + if (pEvdev->flags & EVDEV_RELATIVE_EVENTS) { + + xf86Msg(X_WARNING,"%s: touchpads, tablets and touchscreens ignore " + "relative axes.\n", device->name); + + pEvdev->flags &= ~EVDEV_RELATIVE_EVENTS; + } + + EvdevInitAbsClass(device, pEvdev); +} + +static int +EvdevInit(DeviceIntPtr device) +{ + int i; + InputInfoPtr pInfo; + EvdevPtr pEvdev; + + pInfo = device->public.devicePrivate; + pEvdev = pInfo->private; + + /* clear all axis_map entries */ + for(i = 0; i < max(ABS_CNT,REL_CNT); i++) + pEvdev->axis_map[i]=-1; + + if (pEvdev->flags & EVDEV_KEYBOARD_EVENTS) + EvdevAddKeyClass(device); + if (pEvdev->flags & EVDEV_BUTTON_EVENTS) + EvdevAddButtonClass(device); + + /* We don't allow relative and absolute axes on the same device. The + * reason is that some devices (MS Optical Desktop 2000) register both + * rel and abs axes for x/y. + * + * The abs axes register min/max; this min/max then also applies to the + * relative device (the mouse) and caps it at 0..255 for both axes. + * So, unless you have a small screen, you won't be enjoying it much; + * consequently, absolute axes are generally ignored. + * + * However, currenly only a device with absolute axes can be registered + * as a touch{pad,screen}. Thus, given such a device, absolute axes are + * used and relative axes are ignored. + */ + + if (pEvdev->flags & (EVDEV_UNIGNORE_RELATIVE | EVDEV_UNIGNORE_ABSOLUTE)) + EvdevInitAnyClass(device, pEvdev); + else if (pEvdev->flags & (EVDEV_TOUCHPAD | EVDEV_TOUCHSCREEN | EVDEV_TABLET)) + EvdevInitTouchDevice(device, pEvdev); + else if (pEvdev->flags & EVDEV_RELATIVE_EVENTS) + EvdevInitRelClass(device, pEvdev); +#ifdef _F_INIT_ABS_ONLY_FOR_POINTER_ + else if ( !(pEvdev->flags & EVDEV_KEYBOARD_EVENTS) && (pEvdev->flags & EVDEV_ABSOLUTE_EVENTS) ) +#else + else if (pEvdev->flags & EVDEV_ABSOLUTE_EVENTS) +#endif + EvdevInitAbsClass(device, pEvdev); + +#ifdef HAVE_PROPERTIES + /* We drop the return value, the only time we ever want the handlers to + * unregister is when the device dies. In which case we don't have to + * unregister anyway */ + EvdevInitProperty(device); + XIRegisterPropertyHandler(device, EvdevSetProperty, NULL, NULL); + EvdevMBEmuInitProperty(device); + EvdevWheelEmuInitProperty(device); + EvdevDragLockInitProperty(device); +#endif + + return Success; +} + +/** + * Init all extras (wheel emulation, etc.) and grab the device. + * + * Coming from a resume, the grab may fail with ENODEV. In this case, we set a + * timer to wake up and try to reopen the device later. + */ +static int +EvdevOn(DeviceIntPtr device) +{ + InputInfoPtr pInfo; + EvdevPtr pEvdev; + int rc = 0; + + pInfo = device->public.devicePrivate; + pEvdev = pInfo->private; + + if (pInfo->fd != -1 && pEvdev->grabDevice && + (rc = ioctl(pInfo->fd, EVIOCGRAB, (void *)1))) + { + xf86Msg(X_WARNING, "%s: Grab failed (%s)\n", pInfo->name, + strerror(errno)); + + /* ENODEV - device has disappeared after resume */ + if (rc && errno == ENODEV) + { + close(pInfo->fd); + pInfo->fd = -1; + } + } + + if (pInfo->fd == -1) + { + pEvdev->reopen_left = pEvdev->reopen_attempts; + pEvdev->reopen_timer = TimerSet(pEvdev->reopen_timer, 0, 100, EvdevReopenTimer, pInfo); + } else + { + pEvdev->min_maj = EvdevGetMajorMinor(pInfo); + if (EvdevIsDuplicate(pInfo)) + { + xf86Msg(X_WARNING, "%s: Refusing to enable duplicate device.\n", + pInfo->name); + return !Success; + } + + pEvdev->reopen_timer = TimerSet(pEvdev->reopen_timer, 0, 0, NULL, NULL); + + xf86FlushInput(pInfo->fd); + xf86AddEnabledDevice(pInfo); + EvdevMBEmuOn(pInfo); + pEvdev->flags |= EVDEV_INITIALIZED; + device->public.on = TRUE; + } + + return Success; +} + + +static int +EvdevProc(DeviceIntPtr device, int what) +{ + InputInfoPtr pInfo; + EvdevPtr pEvdev; + + pInfo = device->public.devicePrivate; + pEvdev = pInfo->private; + + switch (what) + { + case DEVICE_INIT: + return EvdevInit(device); + + case DEVICE_ON: + return EvdevOn(device); + + case DEVICE_OFF: + if (pEvdev->flags & EVDEV_INITIALIZED) + EvdevMBEmuFinalize(pInfo); + + if (pInfo->fd != -1) + { + if (pEvdev->grabDevice && ioctl(pInfo->fd, EVIOCGRAB, (void *)0)) + xf86Msg(X_WARNING, "%s: Release failed (%s)\n", pInfo->name, + strerror(errno)); + xf86RemoveEnabledDevice(pInfo); + close(pInfo->fd); + pInfo->fd = -1; + } + pEvdev->min_maj = 0; + pEvdev->flags &= ~EVDEV_INITIALIZED; + device->public.on = FALSE; + if (pEvdev->reopen_timer) + { + TimerFree(pEvdev->reopen_timer); + pEvdev->reopen_timer = NULL; + } + break; + + case DEVICE_CLOSE: + xf86Msg(X_INFO, "%s: Close\n", pInfo->name); + if (pInfo->fd != -1) { + close(pInfo->fd); + pInfo->fd = -1; + } + EvdevRemoveDevice(pInfo); + pEvdev->min_maj = 0; + break; + } + + return Success; +} + +/** + * Get as much information as we can from the fd and cache it. + * If compare is True, then the information retrieved will be compared to the + * one already cached. If the information does not match, then this function + * returns an error. + * + * @return Success if the information was cached, or !Success otherwise. + */ +static int +EvdevCacheCompare(InputInfoPtr pInfo, BOOL compare) +{ + EvdevPtr pEvdev = pInfo->private; + size_t len; + int i; + + char name[1024] = {0}; + unsigned long bitmask[NLONGS(EV_CNT)] = {0}; + unsigned long key_bitmask[NLONGS(KEY_CNT)] = {0}; + unsigned long rel_bitmask[NLONGS(REL_CNT)] = {0}; + unsigned long abs_bitmask[NLONGS(ABS_CNT)] = {0}; + unsigned long led_bitmask[NLONGS(LED_CNT)] = {0}; + + if (ioctl(pInfo->fd, EVIOCGNAME(sizeof(name) - 1), name) < 0) { + xf86Msg(X_ERROR, "ioctl EVIOCGNAME failed: %s\n", strerror(errno)); + goto error; + } + + if (!compare) { + strcpy(pEvdev->name, name); + } else if (strcmp(pEvdev->name, name)) { + xf86Msg(X_ERROR, "%s: device name changed: %s != %s\n", + pInfo->name, pEvdev->name, name); + goto error; + } + + len = ioctl(pInfo->fd, EVIOCGBIT(0, sizeof(bitmask)), bitmask); + if (len < 0) { + xf86Msg(X_ERROR, "%s: ioctl EVIOCGBIT failed: %s\n", + pInfo->name, strerror(errno)); + goto error; + } + + if (!compare) { + memcpy(pEvdev->bitmask, bitmask, len); + } else if (memcmp(pEvdev->bitmask, bitmask, len)) { + xf86Msg(X_ERROR, "%s: device bitmask has changed\n", pInfo->name); + goto error; + } + + len = ioctl(pInfo->fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask); + if (len < 0) { + xf86Msg(X_ERROR, "%s: ioctl EVIOCGBIT failed: %s\n", + pInfo->name, strerror(errno)); + goto error; + } + + if (!compare) { + memcpy(pEvdev->rel_bitmask, rel_bitmask, len); + } else if (memcmp(pEvdev->rel_bitmask, rel_bitmask, len)) { + xf86Msg(X_ERROR, "%s: device rel_bitmask has changed\n", pInfo->name); + goto error; + } + + len = ioctl(pInfo->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask); + if (len < 0) { + xf86Msg(X_ERROR, "%s: ioctl EVIOCGBIT failed: %s\n", + pInfo->name, strerror(errno)); + goto error; + } + + if (!compare) { + memcpy(pEvdev->abs_bitmask, abs_bitmask, len); + } else if (memcmp(pEvdev->abs_bitmask, abs_bitmask, len)) { + xf86Msg(X_ERROR, "%s: device abs_bitmask has changed\n", pInfo->name); + goto error; + } + + len = ioctl(pInfo->fd, EVIOCGBIT(EV_LED, sizeof(led_bitmask)), led_bitmask); + if (len < 0) { + xf86Msg(X_ERROR, "%s: ioctl EVIOCGBIT failed: %s\n", + pInfo->name, strerror(errno)); + goto error; + } + + if (!compare) { + memcpy(pEvdev->led_bitmask, led_bitmask, len); + } else if (memcmp(pEvdev->led_bitmask, led_bitmask, len)) { + xf86Msg(X_ERROR, "%s: device led_bitmask has changed\n", pInfo->name); + goto error; + } + + /* + * Do not try to validate absinfo data since it is not expected + * to be static, always refresh it in evdev structure. + */ + for (i = ABS_X; i <= ABS_MAX; i++) { + if (TestBit(i, abs_bitmask)) { + len = ioctl(pInfo->fd, EVIOCGABS(i), &pEvdev->absinfo[i]); + if (len < 0) { + xf86Msg(X_ERROR, "%s: ioctl EVIOCGABSi(%d) failed: %s\n", + pInfo->name, i, strerror(errno)); + goto error; + } + } + } + + len = ioctl(pInfo->fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask); + if (len < 0) { + xf86Msg(X_ERROR, "%s: ioctl EVIOCGBIT failed: %s\n", + pInfo->name, strerror(errno)); + goto error; + } + + if (compare) { + /* + * Keys are special as user can adjust keymap at any time (on + * devices that support EVIOCSKEYCODE. However we do not expect + * buttons reserved for mice/tablets/digitizers and so on to + * appear/disappear so we will check only those in + * [BTN_MISC, KEY_OK) range. + */ + size_t start_word = BTN_MISC / LONG_BITS; + size_t start_byte = start_word * sizeof(unsigned long); + size_t end_word = KEY_OK / LONG_BITS; + size_t end_byte = end_word * sizeof(unsigned long); + + if (len >= start_byte && + memcmp(&pEvdev->key_bitmask[start_word], &key_bitmask[start_word], + min(len, end_byte) - start_byte + 1)) { + xf86Msg(X_ERROR, "%s: device key_bitmask has changed\n", pInfo->name); + goto error; + } + } + + /* Copy the data so we have reasonably up-to-date info */ + memcpy(pEvdev->key_bitmask, key_bitmask, len); + + return Success; + +error: + return !Success; + +} + +static int +EvdevProbe(InputInfoPtr pInfo) +{ + int i, has_rel_axes, has_abs_axes, has_keys, num_buttons, has_scroll; + int kernel24 = 0; + int ignore_abs = 0, ignore_rel = 0; + EvdevPtr pEvdev = pInfo->private; + + if (pEvdev->grabDevice && ioctl(pInfo->fd, EVIOCGRAB, (void *)1)) { + if (errno == EINVAL) { + /* keyboards are unsafe in 2.4 */ + kernel24 = 1; + pEvdev->grabDevice = 0; + } else { + xf86Msg(X_ERROR, "Grab failed. Device already configured?\n"); + return 1; + } + } else if (pEvdev->grabDevice) { + ioctl(pInfo->fd, EVIOCGRAB, (void *)0); + } + + /* Trinary state for ignoring axes: + - unset: do the normal thing. + - TRUE: explicitly ignore them. + - FALSE: unignore axes, use them at all cost if they're present. + */ + if (xf86FindOption(pInfo->options, "IgnoreRelativeAxes")) + { + if (xf86SetBoolOption(pInfo->options, "IgnoreRelativeAxes", FALSE)) + ignore_rel = TRUE; + else + pEvdev->flags |= EVDEV_UNIGNORE_RELATIVE; + + } + if (xf86FindOption(pInfo->options, "IgnoreAbsoluteAxes")) + { + if (xf86SetBoolOption(pInfo->options, "IgnoreAbsoluteAxes", FALSE)) + ignore_abs = TRUE; + else + pEvdev->flags |= EVDEV_UNIGNORE_ABSOLUTE; + } + + has_rel_axes = FALSE; + has_abs_axes = FALSE; + has_keys = FALSE; + has_scroll = FALSE; + num_buttons = 0; + + /* count all buttons */ + for (i = BTN_MISC; i < BTN_JOYSTICK; i++) + { + int mapping = 0; + if (TestBit(i, pEvdev->key_bitmask)) + { + mapping = EvdevUtilButtonEventToButtonNumber(pEvdev, i); + if (mapping > num_buttons) + num_buttons = mapping; + } + } + + if (num_buttons) + { + pEvdev->flags |= EVDEV_BUTTON_EVENTS; + pEvdev->num_buttons = num_buttons; + xf86Msg(X_INFO, "%s: Found %d mouse buttons\n", pInfo->name, + num_buttons); + } + + for (i = 0; i < REL_MAX; i++) { + if (TestBit(i, pEvdev->rel_bitmask)) { + has_rel_axes = TRUE; + break; + } + } + + if (has_rel_axes) { + if (TestBit(REL_WHEEL, pEvdev->rel_bitmask) || + TestBit(REL_HWHEEL, pEvdev->rel_bitmask) || + TestBit(REL_DIAL, pEvdev->rel_bitmask)) { + xf86Msg(X_INFO, "%s: Found scroll wheel(s)\n", pInfo->name); + has_scroll = TRUE; + if (!num_buttons) + xf86Msg(X_INFO, "%s: Forcing buttons for scroll wheel(s)\n", + pInfo->name); + num_buttons = (num_buttons < 3) ? 7 : num_buttons + 4; + pEvdev->num_buttons = num_buttons; + } + + if (!ignore_rel) + { + xf86Msg(X_INFO, "%s: Found relative axes\n", pInfo->name); + pEvdev->flags |= EVDEV_RELATIVE_EVENTS; + + if (TestBit(REL_X, pEvdev->rel_bitmask) && + TestBit(REL_Y, pEvdev->rel_bitmask)) { + xf86Msg(X_INFO, "%s: Found x and y relative axes\n", pInfo->name); + } + } else { + xf86Msg(X_INFO, "%s: Relative axes present but ignored.\n", pInfo->name); + has_rel_axes = FALSE; + } + } + + for (i = 0; i < ABS_MAX; i++) { + if (TestBit(i, pEvdev->abs_bitmask)) { + has_abs_axes = TRUE; + break; + } + } + + if (ignore_abs && has_abs_axes) + { + xf86Msg(X_INFO, "%s: Absolute axes present but ignored.\n", pInfo->name); + has_abs_axes = FALSE; + } else if (has_abs_axes) { + xf86Msg(X_INFO, "%s: Found absolute axes\n", pInfo->name); + pEvdev->flags |= EVDEV_ABSOLUTE_EVENTS; + + if ((TestBit(ABS_X, pEvdev->abs_bitmask) && + TestBit(ABS_Y, pEvdev->abs_bitmask))) { + xf86Msg(X_INFO, "%s: Found x and y absolute axes\n", pInfo->name); + if (TestBit(BTN_TOOL_PEN, pEvdev->key_bitmask)) + { + xf86Msg(X_INFO, "%s: Found absolute tablet.\n", pInfo->name); + pEvdev->flags |= EVDEV_TABLET; + if (!pEvdev->num_buttons) + { + pEvdev->num_buttons = 7; /* LMR + scroll wheels */ + pEvdev->flags |= EVDEV_BUTTON_EVENTS; + } + } else if (TestBit(ABS_PRESSURE, pEvdev->abs_bitmask) || + TestBit(BTN_TOUCH, pEvdev->key_bitmask)) { + if (num_buttons || TestBit(BTN_TOOL_FINGER, pEvdev->key_bitmask)) { + xf86Msg(X_INFO, "%s: Found absolute touchpad.\n", pInfo->name); + pEvdev->flags |= EVDEV_TOUCHPAD; + memset(pEvdev->old_vals, -1, sizeof(int) * pEvdev->num_vals); + } else { + xf86Msg(X_INFO, "%s: Found absolute touchscreen\n", pInfo->name); + pEvdev->flags |= EVDEV_TOUCHSCREEN; + pEvdev->flags |= EVDEV_BUTTON_EVENTS; + } + } + } + } + + for (i = 0; i < BTN_MISC; i++) { + if (TestBit(i, pEvdev->key_bitmask)) { + xf86Msg(X_INFO, "%s: Found keys\n", pInfo->name); + pEvdev->flags |= EVDEV_KEYBOARD_EVENTS; + has_keys = TRUE; + break; + } + } + + if (has_rel_axes || has_abs_axes || num_buttons) { + pInfo->flags |= XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS | + XI86_CONFIGURED; + if (pEvdev->flags & EVDEV_TOUCHPAD) { + xf86Msg(X_INFO, "%s: Configuring as touchpad\n", pInfo->name); + pInfo->type_name = XI_TOUCHPAD; + } else if (pEvdev->flags & EVDEV_TABLET) { + xf86Msg(X_INFO, "%s: Configuring as tablet\n", pInfo->name); + pInfo->type_name = XI_TABLET; + } else if (pEvdev->flags & EVDEV_TOUCHSCREEN) { + xf86Msg(X_INFO, "%s: Configuring as touchscreen\n", pInfo->name); + pInfo->type_name = XI_TOUCHSCREEN; + } else { + xf86Msg(X_INFO, "%s: Configuring as mouse\n", pInfo->name); + pInfo->type_name = XI_MOUSE; + } + } + + if (has_keys) { + if (kernel24) { + xf86Msg(X_INFO, "%s: Kernel < 2.6 is too old, ignoring keyboard\n", + pInfo->name); + } else { + xf86Msg(X_INFO, "%s: Configuring as keyboard\n", pInfo->name); + pInfo->flags |= XI86_KEYBOARD_CAPABLE | XI86_CONFIGURED; + pInfo->type_name = XI_KEYBOARD; + } + } + + if (has_scroll && (pInfo->flags & XI86_CONFIGURED) && + (pInfo->flags & XI86_POINTER_CAPABLE) == 0) + { + xf86Msg(X_INFO, "%s: Adding scrollwheel support\n", pInfo->name); + pInfo->flags |= XI86_POINTER_CAPABLE; + pEvdev->flags |= EVDEV_BUTTON_EVENTS; + pEvdev->flags |= EVDEV_RELATIVE_EVENTS; + } + + if ((pInfo->flags & XI86_CONFIGURED) == 0) { + xf86Msg(X_WARNING, "%s: Don't know how to use device\n", + pInfo->name); + return 1; + } + + return 0; +} + +static void +EvdevSetCalibration(InputInfoPtr pInfo, int num_calibration, int calibration[4]) +{ + EvdevPtr pEvdev = pInfo->private; + + if (num_calibration == 0) { + pEvdev->flags &= ~EVDEV_CALIBRATED; + pEvdev->calibration.min_x = 0; + pEvdev->calibration.max_x = 0; + pEvdev->calibration.min_y = 0; + pEvdev->calibration.max_y = 0; + } else if (num_calibration == 4) { + pEvdev->flags |= EVDEV_CALIBRATED; + pEvdev->calibration.min_x = calibration[0]; + pEvdev->calibration.max_x = calibration[1]; + pEvdev->calibration.min_y = calibration[2]; + pEvdev->calibration.max_y = calibration[3]; + } +} + +static InputInfoPtr +EvdevPreInit(InputDriverPtr drv, IDevPtr dev, int flags) +{ + InputInfoPtr pInfo; + const char *device, *str; + int num_calibration = 0, calibration[4] = { 0, 0, 0, 0 }; + int num_resolution = 0, resolution[4] = { 0, 0, 0, 0 }; + EvdevPtr pEvdev; + + if (!(pInfo = xf86AllocateInput(drv, 0))) + return NULL; + + /* Initialise the InputInfoRec. */ + pInfo->name = dev->identifier; + pInfo->flags = 0; + pInfo->type_name = "UNKNOWN"; + pInfo->device_control = EvdevProc; + pInfo->read_input = EvdevReadInput; + pInfo->history_size = 0; + pInfo->control_proc = NULL; + pInfo->close_proc = NULL; + pInfo->switch_mode = NULL; + pInfo->conversion_proc = NULL; + pInfo->reverse_conversion_proc = NULL; + pInfo->dev = NULL; + pInfo->private_flags = 0; + pInfo->always_core_feedback = NULL; + pInfo->conf_idev = dev; + + if (!(pEvdev = xcalloc(sizeof(EvdevRec), 1))) + return pInfo; + + pInfo->private = pEvdev; + + xf86CollectInputOptions(pInfo, evdevDefaults, NULL); + xf86ProcessCommonOptions(pInfo, pInfo->options); + + /* + * We initialize pEvdev->tool to 1 so that device that doesn't use + * proximity will still report events. + */ + pEvdev->tool = 1; + + device = xf86CheckStrOption(dev->commonOptions, "Device", NULL); + if (!device) { + xf86Msg(X_ERROR, "%s: No device specified.\n", pInfo->name); + xf86DeleteInput(pInfo, 0); + return NULL; + } + + pEvdev->device = device; + + xf86Msg(X_CONFIG, "%s: Device: \"%s\"\n", pInfo->name, device); + do { + pInfo->fd = open(device, O_RDWR | O_NONBLOCK, 0); + } while (pInfo->fd < 0 && errno == EINTR); + + if (pInfo->fd < 0) { + xf86Msg(X_ERROR, "Unable to open evdev device \"%s\".\n", device); + xf86DeleteInput(pInfo, 0); + return NULL; + } + + /* Check major/minor of device node to avoid adding duplicate devices. */ + pEvdev->min_maj = EvdevGetMajorMinor(pInfo); + if (EvdevIsDuplicate(pInfo)) + { + xf86Msg(X_WARNING, "%s: device file already in use. Ignoring.\n", + pInfo->name); + close(pInfo->fd); + xf86DeleteInput(pInfo, 0); + return NULL; + } + + pEvdev->reopen_attempts = xf86SetIntOption(pInfo->options, "ReopenAttempts", 10); + pEvdev->invert_x = xf86SetBoolOption(pInfo->options, "InvertX", FALSE); + pEvdev->invert_y = xf86SetBoolOption(pInfo->options, "InvertY", FALSE); + pEvdev->swap_axes = xf86SetBoolOption(pInfo->options, "SwapAxes", FALSE); + + str = xf86CheckStrOption(pInfo->options, "Calibration", NULL); + if (str) { + num_calibration = sscanf(str, "%d %d %d %d", + &calibration[0], &calibration[1], + &calibration[2], &calibration[3]); + if (num_calibration == 4) + EvdevSetCalibration(pInfo, num_calibration, calibration); + else + xf86Msg(X_ERROR, + "%s: Insufficient calibration factors (%d). Ignoring calibration\n", + pInfo->name, num_calibration); + } + + str = xf86CheckStrOption(pInfo->options, "Resolution", NULL); + if (str) { + num_resolution = sscanf(str, "%d %d %d %d", + &resolution[0], &resolution[1], + &resolution[2], &resolution[3]); + if (num_resolution == 4) + EvdevSetResolution(pInfo, num_resolution, resolution); + else + xf86Msg(X_ERROR, + "%s: Insufficient resolution factors (%d). Ignoring resolution\n", + pInfo->name, num_resolution); + } + + /* Grabbing the event device stops in-kernel event forwarding. In other + words, it disables rfkill and the "Macintosh mouse button emulation". + Note that this needs a server that sets the console to RAW mode. */ + pEvdev->grabDevice = xf86CheckBoolOption(dev->commonOptions, "GrabDevice", 0); + + EvdevInitButtonMapping(pInfo); + + if (EvdevCacheCompare(pInfo, FALSE) || + EvdevProbe(pInfo)) { + close(pInfo->fd); + xf86DeleteInput(pInfo, 0); + return NULL; + } + + if(pEvdev->flags & EVDEV_RESOLUTION) + { + EvdevSwapAxes(pEvdev); + } + else + { + pEvdev->absinfo[ABS_X].maximum = 0; + pEvdev->absinfo[ABS_X].minimum = 0; + pEvdev->absinfo[ABS_Y].maximum = 0; + pEvdev->absinfo[ABS_Y].minimum = 0; + } + + EvdevAddDevice(pInfo); + + if (pEvdev->flags & EVDEV_BUTTON_EVENTS) + { + EvdevMBEmuPreInit(pInfo); + EvdevWheelEmuPreInit(pInfo); + EvdevDragLockPreInit(pInfo); + } + + return pInfo; +} + +_X_EXPORT InputDriverRec EVDEV = { + 1, + "evdev", + NULL, + EvdevPreInit, + NULL, + NULL, + 0 +}; + +static void +EvdevUnplug(pointer p) +{ +} + +static pointer +EvdevPlug(pointer module, + pointer options, + int *errmaj, + int *errmin) +{ + xf86AddInputDriver(&EVDEV, module, 0); + return module; +} + +static XF86ModuleVersionInfo EvdevVersionRec = +{ + "evdev", + MODULEVENDORSTRING, + MODINFOSTRING1, + MODINFOSTRING2, + XORG_VERSION_CURRENT, + PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCHLEVEL, + ABI_CLASS_XINPUT, + ABI_XINPUT_VERSION, + MOD_CLASS_XINPUT, + {0, 0, 0, 0} +}; + +_X_EXPORT XF86ModuleData evdevModuleData = +{ + &EvdevVersionRec, + EvdevPlug, + EvdevUnplug +}; + + +/* Return an index value for a given button event code + * returns 0 on non-button event. + */ +unsigned int +EvdevUtilButtonEventToButtonNumber(EvdevPtr pEvdev, int code) +{ + unsigned int button = 0; + + switch(code) { + case BTN_LEFT: + button = 1; + break; + + case BTN_RIGHT: + button = 3; + break; + + case BTN_MIDDLE: + button = 2; + break; + + /* Treat BTN_[0-2] as LMR buttons on devices that do not advertise + BTN_LEFT, BTN_MIDDLE, BTN_RIGHT. + Otherwise, treat BTN_[0+n] as button 5+n. + XXX: This causes duplicate mappings for BTN_0 + n and BTN_SIDE + n + */ + case BTN_0: + button = (TestBit(BTN_LEFT, pEvdev->key_bitmask)) ? 8 : 1; + break; + case BTN_1: + button = (TestBit(BTN_MIDDLE, pEvdev->key_bitmask)) ? 9 : 2; + break; + case BTN_2: + button = (TestBit(BTN_RIGHT, pEvdev->key_bitmask)) ? 10 : 3; + break; + + /* FIXME: BTN_3.. and BTN_SIDE.. have the same button mapping */ + case BTN_3: + case BTN_4: + case BTN_5: + case BTN_6: + case BTN_7: + case BTN_8: + case BTN_9: + button = (code - BTN_0 + 5); + break; + + case BTN_SIDE: + case BTN_EXTRA: + case BTN_FORWARD: + case BTN_BACK: + case BTN_TASK: + button = (code - BTN_LEFT + 5); + break; + + default: + if ((code > BTN_TASK) && (code < KEY_OK)) { + if (code < BTN_JOYSTICK) { + if (code < BTN_MOUSE) + button = (code - BTN_0 + 5); + else + button = (code - BTN_LEFT + 5); + } + } + } + + if (button > EVDEV_MAXBUTTONS) + return 0; + + return button; +} + +#ifdef HAVE_PROPERTIES +#ifdef HAVE_LABELS +/* Aligned with linux/input.h. + Note that there are holes in the ABS_ range, these are simply replaced with + MISC here */ +static char* abs_labels[] = { + AXIS_LABEL_PROP_ABS_X, /* 0x00 */ + AXIS_LABEL_PROP_ABS_Y, /* 0x01 */ + AXIS_LABEL_PROP_ABS_Z, /* 0x02 */ + AXIS_LABEL_PROP_ABS_RX, /* 0x03 */ + AXIS_LABEL_PROP_ABS_RY, /* 0x04 */ + AXIS_LABEL_PROP_ABS_RZ, /* 0x05 */ + AXIS_LABEL_PROP_ABS_THROTTLE, /* 0x06 */ + AXIS_LABEL_PROP_ABS_RUDDER, /* 0x07 */ + AXIS_LABEL_PROP_ABS_WHEEL, /* 0x08 */ + AXIS_LABEL_PROP_ABS_GAS, /* 0x09 */ + AXIS_LABEL_PROP_ABS_BRAKE, /* 0x0a */ + AXIS_LABEL_PROP_ABS_MISC, /* undefined */ + AXIS_LABEL_PROP_ABS_MISC, /* undefined */ + AXIS_LABEL_PROP_ABS_MISC, /* undefined */ + AXIS_LABEL_PROP_ABS_MISC, /* undefined */ + AXIS_LABEL_PROP_ABS_MISC, /* undefined */ + AXIS_LABEL_PROP_ABS_HAT0X, /* 0x10 */ + AXIS_LABEL_PROP_ABS_HAT0Y, /* 0x11 */ + AXIS_LABEL_PROP_ABS_HAT1X, /* 0x12 */ + AXIS_LABEL_PROP_ABS_HAT1Y, /* 0x13 */ + AXIS_LABEL_PROP_ABS_HAT2X, /* 0x14 */ + AXIS_LABEL_PROP_ABS_HAT2Y, /* 0x15 */ + AXIS_LABEL_PROP_ABS_HAT3X, /* 0x16 */ + AXIS_LABEL_PROP_ABS_HAT3Y, /* 0x17 */ + AXIS_LABEL_PROP_ABS_PRESSURE, /* 0x18 */ + AXIS_LABEL_PROP_ABS_DISTANCE, /* 0x19 */ + AXIS_LABEL_PROP_ABS_TILT_X, /* 0x1a */ + AXIS_LABEL_PROP_ABS_TILT_Y, /* 0x1b */ + AXIS_LABEL_PROP_ABS_TOOL_WIDTH, /* 0x1c */ + AXIS_LABEL_PROP_ABS_MISC, /* undefined */ + AXIS_LABEL_PROP_ABS_MISC, /* undefined */ + AXIS_LABEL_PROP_ABS_MISC, /* undefined */ + AXIS_LABEL_PROP_ABS_VOLUME /* 0x20 */ +}; + +static char* rel_labels[] = { + AXIS_LABEL_PROP_REL_X, + AXIS_LABEL_PROP_REL_Y, + AXIS_LABEL_PROP_REL_Z, + AXIS_LABEL_PROP_REL_RX, + AXIS_LABEL_PROP_REL_RY, + AXIS_LABEL_PROP_REL_RZ, + AXIS_LABEL_PROP_REL_HWHEEL, + AXIS_LABEL_PROP_REL_DIAL, + AXIS_LABEL_PROP_REL_WHEEL, + AXIS_LABEL_PROP_REL_MISC +}; + +static char* btn_labels[][16] = { + { /* BTN_MISC group offset 0x100*/ + BTN_LABEL_PROP_BTN_0, /* 0x00 */ + BTN_LABEL_PROP_BTN_1, /* 0x01 */ + BTN_LABEL_PROP_BTN_2, /* 0x02 */ + BTN_LABEL_PROP_BTN_3, /* 0x03 */ + BTN_LABEL_PROP_BTN_4, /* 0x04 */ + BTN_LABEL_PROP_BTN_5, /* 0x05 */ + BTN_LABEL_PROP_BTN_6, /* 0x06 */ + BTN_LABEL_PROP_BTN_7, /* 0x07 */ + BTN_LABEL_PROP_BTN_8, /* 0x08 */ + BTN_LABEL_PROP_BTN_9 /* 0x09 */ + }, + { /* BTN_MOUSE group offset 0x110 */ + BTN_LABEL_PROP_BTN_LEFT, /* 0x00 */ + BTN_LABEL_PROP_BTN_RIGHT, /* 0x01 */ + BTN_LABEL_PROP_BTN_MIDDLE, /* 0x02 */ + BTN_LABEL_PROP_BTN_SIDE, /* 0x03 */ + BTN_LABEL_PROP_BTN_EXTRA, /* 0x04 */ + BTN_LABEL_PROP_BTN_FORWARD, /* 0x05 */ + BTN_LABEL_PROP_BTN_BACK, /* 0x06 */ + BTN_LABEL_PROP_BTN_TASK /* 0x07 */ + }, + { /* BTN_JOYSTICK group offset 0x120 */ + BTN_LABEL_PROP_BTN_TRIGGER, /* 0x00 */ + BTN_LABEL_PROP_BTN_THUMB, /* 0x01 */ + BTN_LABEL_PROP_BTN_THUMB2, /* 0x02 */ + BTN_LABEL_PROP_BTN_TOP, /* 0x03 */ + BTN_LABEL_PROP_BTN_TOP2, /* 0x04 */ + BTN_LABEL_PROP_BTN_PINKIE, /* 0x05 */ + BTN_LABEL_PROP_BTN_BASE, /* 0x06 */ + BTN_LABEL_PROP_BTN_BASE2, /* 0x07 */ + BTN_LABEL_PROP_BTN_BASE3, /* 0x08 */ + BTN_LABEL_PROP_BTN_BASE4, /* 0x09 */ + BTN_LABEL_PROP_BTN_BASE5, /* 0x0a */ + BTN_LABEL_PROP_BTN_BASE6, /* 0x0b */ + NULL, + NULL, + NULL, + BTN_LABEL_PROP_BTN_DEAD /* 0x0f */ + }, + { /* BTN_GAMEPAD group offset 0x130 */ + BTN_LABEL_PROP_BTN_A, /* 0x00 */ + BTN_LABEL_PROP_BTN_B, /* 0x01 */ + BTN_LABEL_PROP_BTN_C, /* 0x02 */ + BTN_LABEL_PROP_BTN_X, /* 0x03 */ + BTN_LABEL_PROP_BTN_Y, /* 0x04 */ + BTN_LABEL_PROP_BTN_Z, /* 0x05 */ + BTN_LABEL_PROP_BTN_TL, /* 0x06 */ + BTN_LABEL_PROP_BTN_TR, /* 0x07 */ + BTN_LABEL_PROP_BTN_TL2, /* 0x08 */ + BTN_LABEL_PROP_BTN_TR2, /* 0x09 */ + BTN_LABEL_PROP_BTN_SELECT, /* 0x0a */ + BTN_LABEL_PROP_BTN_START, /* 0x0b */ + BTN_LABEL_PROP_BTN_MODE, /* 0x0c */ + BTN_LABEL_PROP_BTN_THUMBL, /* 0x0d */ + BTN_LABEL_PROP_BTN_THUMBR /* 0x0e */ + }, + { /* BTN_DIGI group offset 0x140 */ + BTN_LABEL_PROP_BTN_TOOL_PEN, /* 0x00 */ + BTN_LABEL_PROP_BTN_TOOL_RUBBER, /* 0x01 */ + BTN_LABEL_PROP_BTN_TOOL_BRUSH, /* 0x02 */ + BTN_LABEL_PROP_BTN_TOOL_PENCIL, /* 0x03 */ + BTN_LABEL_PROP_BTN_TOOL_AIRBRUSH, /* 0x04 */ + BTN_LABEL_PROP_BTN_TOOL_FINGER, /* 0x05 */ + BTN_LABEL_PROP_BTN_TOOL_MOUSE, /* 0x06 */ + BTN_LABEL_PROP_BTN_TOOL_LENS, /* 0x07 */ + NULL, + NULL, + BTN_LABEL_PROP_BTN_TOUCH, /* 0x0a */ + BTN_LABEL_PROP_BTN_STYLUS, /* 0x0b */ + BTN_LABEL_PROP_BTN_STYLUS2, /* 0x0c */ + BTN_LABEL_PROP_BTN_TOOL_DOUBLETAP, /* 0x0d */ + BTN_LABEL_PROP_BTN_TOOL_TRIPLETAP /* 0x0e */ + }, + { /* BTN_WHEEL group offset 0x150 */ + BTN_LABEL_PROP_BTN_GEAR_DOWN, /* 0x00 */ + BTN_LABEL_PROP_BTN_GEAR_UP /* 0x01 */ + } +}; + +#endif /* HAVE_LABELS */ + +static void EvdevInitAxesLabels(EvdevPtr pEvdev, int natoms, Atom *atoms) +{ +#ifdef HAVE_LABELS + Atom atom; + int axis; + char **labels; + int labels_len = 0; + char *misc_label; + + if (pEvdev->flags & EVDEV_ABSOLUTE_EVENTS) + { + labels = abs_labels; + labels_len = ArrayLength(abs_labels); + misc_label = AXIS_LABEL_PROP_ABS_MISC; + } else if ((pEvdev->flags & EVDEV_RELATIVE_EVENTS)) + { + labels = rel_labels; + labels_len = ArrayLength(rel_labels); + misc_label = AXIS_LABEL_PROP_REL_MISC; + } + + memset(atoms, 0, natoms * sizeof(Atom)); + + /* Now fill the ones we know */ + for (axis = 0; axis < labels_len; axis++) + { + if (pEvdev->axis_map[axis] == -1) + continue; + + atom = XIGetKnownProperty(labels[axis]); + if (!atom) /* Should not happen */ + continue; + + atoms[pEvdev->axis_map[axis]] = atom; + } +#endif +} + +static void EvdevInitButtonLabels(EvdevPtr pEvdev, int natoms, Atom *atoms) +{ +#ifdef HAVE_LABELS + Atom atom; + int button, bmap; + + /* First, make sure all atoms are initialized */ + atom = XIGetKnownProperty(BTN_LABEL_PROP_BTN_UNKNOWN); + for (button = 0; button < natoms; button++) + atoms[button] = atom; + + for (button = BTN_MISC; button < BTN_JOYSTICK; button++) + { + if (TestBit(button, pEvdev->key_bitmask)) + { + int group = (button % 0x100)/16; + int idx = button - ((button/16) * 16); + + if (!btn_labels[group][idx]) + continue; + + atom = XIGetKnownProperty(btn_labels[group][idx]); + if (!atom) + continue; + + /* Props are 0-indexed, button numbers start with 1 */ + bmap = EvdevUtilButtonEventToButtonNumber(pEvdev, button) - 1; + atoms[bmap] = atom; + } + } + + /* wheel buttons, hardcoded anyway */ + if (natoms > 3) + atoms[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP); + if (natoms > 4) + atoms[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN); + if (natoms > 5) + atoms[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT); + if (natoms > 6) + atoms[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT); +#endif +} + +static void +EvdevInitProperty(DeviceIntPtr dev) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + int rc; + BOOL invert[2]; + char reopen; + + prop_reopen = MakeAtom(EVDEV_PROP_REOPEN, strlen(EVDEV_PROP_REOPEN), + TRUE); + + reopen = pEvdev->reopen_attempts; + rc = XIChangeDeviceProperty(dev, prop_reopen, XA_INTEGER, 8, + PropModeReplace, 1, &reopen, FALSE); + if (rc != Success) + return; + + XISetDevicePropertyDeletable(dev, prop_reopen, FALSE); + + if (pEvdev->flags & (EVDEV_RELATIVE_EVENTS | EVDEV_ABSOLUTE_EVENTS)) + { + invert[0] = pEvdev->invert_x; + invert[1] = pEvdev->invert_y; + + prop_invert = MakeAtom(EVDEV_PROP_INVERT_AXES, strlen(EVDEV_PROP_INVERT_AXES), TRUE); + + rc = XIChangeDeviceProperty(dev, prop_invert, XA_INTEGER, 8, + PropModeReplace, 2, + invert, FALSE); + if (rc != Success) + return; + + XISetDevicePropertyDeletable(dev, prop_invert, FALSE); + + prop_calibration = MakeAtom(EVDEV_PROP_CALIBRATION, + strlen(EVDEV_PROP_CALIBRATION), TRUE); + if (pEvdev->flags & EVDEV_CALIBRATED) { + int calibration[4]; + + calibration[0] = pEvdev->calibration.min_x; + calibration[1] = pEvdev->calibration.max_x; + calibration[2] = pEvdev->calibration.min_y; + calibration[3] = pEvdev->calibration.max_y; + + rc = XIChangeDeviceProperty(dev, prop_calibration, XA_INTEGER, + 32, PropModeReplace, 4, calibration, + FALSE); + } else if (pEvdev->flags & EVDEV_ABSOLUTE_EVENTS) { + rc = XIChangeDeviceProperty(dev, prop_calibration, XA_INTEGER, + 32, PropModeReplace, 0, NULL, + FALSE); + } + if (rc != Success) + return; + + XISetDevicePropertyDeletable(dev, prop_calibration, FALSE); + + prop_swap = MakeAtom(EVDEV_PROP_SWAP_AXES, + strlen(EVDEV_PROP_SWAP_AXES), TRUE); + + rc = XIChangeDeviceProperty(dev, prop_swap, XA_INTEGER, 8, + PropModeReplace, 1, &pEvdev->swap_axes, FALSE); + if (rc != Success) + return; + + XISetDevicePropertyDeletable(dev, prop_swap, FALSE); + +#ifdef HAVE_LABELS + /* Axis labelling */ + if ((pEvdev->num_vals > 0) && (prop_axis_label = XIGetKnownProperty(AXIS_LABEL_PROP))) + { + Atom atoms[pEvdev->num_vals]; + EvdevInitAxesLabels(pEvdev, pEvdev->num_vals, atoms); + XIChangeDeviceProperty(dev, prop_axis_label, XA_ATOM, 32, + PropModeReplace, pEvdev->num_vals, atoms, FALSE); + XISetDevicePropertyDeletable(dev, prop_axis_label, FALSE); + } + /* Button labelling */ + if ((pEvdev->num_buttons > 0) && (prop_btn_label = XIGetKnownProperty(BTN_LABEL_PROP))) + { + Atom atoms[EVDEV_MAXBUTTONS]; + EvdevInitButtonLabels(pEvdev, EVDEV_MAXBUTTONS, atoms); + XIChangeDeviceProperty(dev, prop_btn_label, XA_ATOM, 32, + PropModeReplace, pEvdev->num_buttons, atoms, FALSE); + XISetDevicePropertyDeletable(dev, prop_btn_label, FALSE); + } +#endif /* HAVE_LABELS */ + } + +} + +static void EvdevSwapAxes(EvdevPtr pEvdev) +{ + if(pEvdev->swap_axes) + { + pEvdev->absinfo[ABS_Y].maximum = pEvdev->resolution.min_x; + pEvdev->absinfo[ABS_Y].minimum = pEvdev->resolution.max_x; + pEvdev->absinfo[ABS_X].maximum = pEvdev->resolution.min_y; + pEvdev->absinfo[ABS_X].minimum = pEvdev->resolution.max_y; + } + else + { + pEvdev->absinfo[ABS_X].maximum = pEvdev->resolution.min_x; + pEvdev->absinfo[ABS_X].minimum = pEvdev->resolution.max_x; + pEvdev->absinfo[ABS_Y].maximum = pEvdev->resolution.min_y; + pEvdev->absinfo[ABS_Y].minimum = pEvdev->resolution.max_y; + } +} + +static void +EvdevSetResolution(InputInfoPtr pInfo, int num_resolution, int resolution[4]) +{ + EvdevPtr pEvdev = pInfo->private; + + if (num_resolution == 0) { + pEvdev->flags &= ~EVDEV_RESOLUTION; + pEvdev->resolution.min_x = 0; + pEvdev->resolution.max_x = 0; + pEvdev->resolution.min_y = 0; + pEvdev->resolution.max_y = 0; + } else if (num_resolution == 4) { + pEvdev->flags |= EVDEV_RESOLUTION; + pEvdev->resolution.min_x = resolution[0]; + pEvdev->resolution.max_x = resolution[1]; + pEvdev->resolution.min_y = resolution[2]; + pEvdev->resolution.max_y = resolution[3]; + } +} + +static int +EvdevSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, + BOOL checkonly) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + + if (atom == prop_invert) + { + BOOL* data; + if (val->format != 8 || val->size != 2 || val->type != XA_INTEGER) + return BadMatch; + + if (!checkonly) + { + data = (BOOL*)val->data; + pEvdev->invert_x = data[0]; + pEvdev->invert_y = data[1]; + } + } else if (atom == prop_reopen) + { + if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER) + return BadMatch; + + if (!checkonly) + pEvdev->reopen_attempts = *((CARD8*)val->data); + } else if (atom == prop_calibration) + { + if (val->format != 32 || val->type != XA_INTEGER) + return BadMatch; + if (val->size != 4 && val->size != 0) + return BadMatch; + + if (!checkonly) + EvdevSetCalibration(pInfo, val->size, val->data); + } else if (atom == prop_swap) + { + if (val->format != 8 || val->type != XA_INTEGER || val->size != 1) + return BadMatch; + + if (!checkonly) + pEvdev->swap_axes = *((BOOL*)val->data); + EvdevSwapAxes(pEvdev); + } else if (atom == prop_axis_label || atom == prop_btn_label) + return BadAccess; /* Axis/Button labels can't be changed */ + + return Success; +} +#endif diff --git a/src/evdev.h b/src/evdev.h new file mode 100644 index 0000000..e702694 --- /dev/null +++ b/src/evdev.h @@ -0,0 +1,225 @@ +/* + * Copyright © 2004-2008 Red Hat, Inc. + * Copyright © 2008 University of South Australia + * + * 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 Red Hat + * not be used in advertising or publicity pertaining to distribution + * of the software without specific, written prior permission. Red + * Hat makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS 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. + * + * Authors: + * Kristian Høgsberg (krh@redhat.com) + * Adam Jackson (ajax@redhat.com) + * Peter Hutterer (peter@cs.unisa.edu.au) + * Oliver McFadden (oliver.mcfadden@nokia.com) + */ + +#ifndef EVDEV_H +#define EVDEV_H + +#include <linux/input.h> +#include <linux/types.h> + +#include <xf86Xinput.h> +#include <xf86_OSproc.h> +#include <xkbstr.h> + +#ifndef EV_CNT /* linux 2.4 kernels and earlier lack _CNT defines */ +#define EV_CNT (EV_MAX+1) +#endif +#ifndef KEY_CNT +#define KEY_CNT (KEY_MAX+1) +#endif +#ifndef REL_CNT +#define REL_CNT (REL_MAX+1) +#endif +#ifndef ABS_CNT +#define ABS_CNT (ABS_MAX+1) +#endif +#ifndef LED_CNT +#define LED_CNT (LED_MAX+1) +#endif + +#define EVDEV_MAXBUTTONS 32 +#define EVDEV_MAXQUEUE 32 + +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3 +#define HAVE_PROPERTIES 1 +#endif + +#ifndef MAX_VALUATORS +#define MAX_VALUATORS 36 +#endif + + +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5 +typedef struct { + char *rules; + char *model; + char *layout; + char *variant; + char *options; +} XkbRMLVOSet; +#endif + + +#define LONG_BITS (sizeof(long) * 8) + +/* Number of longs needed to hold the given number of bits */ +#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) + +/* axis specific data for wheel emulation */ +typedef struct { + int up_button; + int down_button; + int traveled_distance; +} WheelAxis, *WheelAxisPtr; + +/* Event queue used to defer keyboard/button events until EV_SYN time. */ +typedef struct { + enum { + EV_QUEUE_KEY, /* xf86PostKeyboardEvent() */ + EV_QUEUE_BTN, /* xf86PostButtonEvent() */ + } type; + int key; /* May be either a key code or button number. */ + int val; /* State of the key/button; pressed or released. */ +} EventQueueRec, *EventQueuePtr; + +typedef struct { + const char *device; + int grabDevice; /* grab the event device? */ + + int num_vals; /* number of valuators */ + int axis_map[max(ABS_CNT, REL_CNT)]; /* Map evdev <axis> to index */ + int vals[MAX_VALUATORS]; + int old_vals[MAX_VALUATORS]; /* Translate absolute inputs to relative */ + + int flags; + int tool; + int num_buttons; /* number of buttons */ + BOOL swap_axes; + BOOL invert_x; + BOOL invert_y; + + int delta[REL_CNT]; + unsigned int abs, rel; + + /* XKB stuff has to be per-device rather than per-driver */ +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5 + XkbComponentNamesRec xkbnames; +#endif + XkbRMLVOSet rmlvo; + + /* Middle mouse button emulation */ + struct { + BOOL enabled; + BOOL pending; /* timer waiting? */ + int buttonstate; /* phys. button state */ + int state; /* state machine (see bt3emu.c) */ + Time expires; /* time of expiry */ + Time timeout; + } emulateMB; + struct { + int meta; /* meta key to lock any button */ + BOOL meta_state; /* meta_button state */ + unsigned int lock_pair[EVDEV_MAXBUTTONS]; /* specify a meta/lock pair */ + BOOL lock_state[EVDEV_MAXBUTTONS]; /* state of any locked buttons */ + } dragLock; + struct { + BOOL enabled; + int button; + int button_state; + int inertia; + WheelAxis X; + WheelAxis Y; + Time expires; /* time of expiry */ + Time timeout; + } emulateWheel; + /* run-time calibration */ + struct { + int min_x; + int max_x; + int min_y; + int max_y; + } calibration; + + struct { + int min_x; + int max_x; + int min_y; + int max_y; + } resolution; + + unsigned char btnmap[32]; /* config-file specified button mapping */ + + int reopen_attempts; /* max attempts to re-open after read failure */ + int reopen_left; /* number of attempts left to re-open the device */ + OsTimerPtr reopen_timer; + + /* Cached info from device. */ + char name[1024]; + unsigned long bitmask[NLONGS(EV_CNT)]; + unsigned long key_bitmask[NLONGS(KEY_CNT)]; + unsigned long rel_bitmask[NLONGS(REL_CNT)]; + unsigned long abs_bitmask[NLONGS(ABS_CNT)]; + unsigned long led_bitmask[NLONGS(LED_CNT)]; + struct input_absinfo absinfo[ABS_CNT]; + + /* minor/major number */ + dev_t min_maj; + + /* Event queue used to defer keyboard/button events until EV_SYN time. */ + int num_queue; + EventQueueRec queue[EVDEV_MAXQUEUE]; +} EvdevRec, *EvdevPtr; + +/* Event posting functions */ +void EvdevQueueKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value); +void EvdevQueueButtonEvent(InputInfoPtr pInfo, int button, int value); +void EvdevPostButtonEvent(InputInfoPtr pInfo, int button, int value); +void EvdevQueueButtonClicks(InputInfoPtr pInfo, int button, int count); +void EvdevPostRelativeMotionEvents(InputInfoPtr pInfo, int *num_v, int *first_v, + int v[MAX_VALUATORS]); +void EvdevPostAbsoluteMotionEvents(InputInfoPtr pInfo, int *num_v, int *first_v, + int v[MAX_VALUATORS]); +unsigned int EvdevUtilButtonEventToButtonNumber(EvdevPtr pEvdev, int code); + +/* Middle Button emulation */ +int EvdevMBEmuTimer(InputInfoPtr); +BOOL EvdevMBEmuFilterEvent(InputInfoPtr, int, BOOL); +void EvdevMBEmuWakeupHandler(pointer, int, pointer); +void EvdevMBEmuBlockHandler(pointer, struct timeval**, pointer); +void EvdevMBEmuPreInit(InputInfoPtr); +void EvdevMBEmuOn(InputInfoPtr); +void EvdevMBEmuFinalize(InputInfoPtr); +void EvdevMBEmuEnable(InputInfoPtr, BOOL); + +/* Mouse Wheel emulation */ +void EvdevWheelEmuPreInit(InputInfoPtr pInfo); +BOOL EvdevWheelEmuFilterButton(InputInfoPtr pInfo, unsigned int button, int value); +BOOL EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv); + +/* Draglock code */ +void EvdevDragLockPreInit(InputInfoPtr pInfo); +BOOL EvdevDragLockFilterEvent(InputInfoPtr pInfo, unsigned int button, int value); + +#ifdef HAVE_PROPERTIES +void EvdevMBEmuInitProperty(DeviceIntPtr); +void EvdevWheelEmuInitProperty(DeviceIntPtr); +void EvdevDragLockInitProperty(DeviceIntPtr); +#endif +#endif diff --git a/xorg-evdev.pc.in b/xorg-evdev.pc.in new file mode 100644 index 0000000..20710a6 --- /dev/null +++ b/xorg-evdev.pc.in @@ -0,0 +1,6 @@ +sdkdir=@sdkdir@ + +Name: xorg-evdev +Description: X.Org evdev input driver. +Version: @PACKAGE_VERSION@ +Cflags: -I${sdkdir} |