summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2012-11-06 21:15:37 -0800
committerAnas Nashif <anas.nashif@intel.com>2012-11-06 21:15:37 -0800
commitf284c5b66d28e14e5934c2cac066afa1c9f49a88 (patch)
tree345168db809340cb1b7b87facacc54982d8020af
downloadxf86-input-synaptics-f284c5b66d28e14e5934c2cac066afa1c9f49a88.tar.gz
xf86-input-synaptics-f284c5b66d28e14e5934c2cac066afa1c9f49a88.tar.bz2
xf86-input-synaptics-f284c5b66d28e14e5934c2cac066afa1c9f49a88.zip
Imported Upstream version 1.6.2upstream/1.6.2
-rw-r--r--.gitignore78
-rw-r--r--COPYING40
-rw-r--r--Makefile.am41
-rw-r--r--README152
-rwxr-xr-xautogen.sh12
-rw-r--r--conf/11-x11-synaptics.fdi35
-rw-r--r--conf/50-synaptics.conf44
-rw-r--r--conf/Makefile.am27
-rw-r--r--configure.ac176
-rw-r--r--docs/README.alps77
-rw-r--r--docs/tapndrag.dia1827
-rw-r--r--docs/trouble-shooting.txt152
-rw-r--r--include/Makefile.am21
-rw-r--r--include/synaptics-properties.h167
-rw-r--r--include/synaptics.h71
-rw-r--r--man/Makefile.am47
-rw-r--r--man/synaptics.man1029
-rw-r--r--man/synclient.man116
-rw-r--r--man/syndaemon.man101
-rw-r--r--src/Makefile.am55
-rw-r--r--src/alpscomm.c233
-rw-r--r--src/eventcomm.c997
-rw-r--r--src/eventcomm.h48
-rw-r--r--src/properties.c844
-rw-r--r--src/ps2comm.c673
-rw-r--r--src/ps2comm.h111
-rw-r--r--src/psmcomm.c175
-rw-r--r--src/synaptics.c3529
-rw-r--r--src/synapticsstr.h292
-rw-r--r--src/synproto.c183
-rw-r--r--src/synproto.h129
-rw-r--r--test/.gitignore6
-rw-r--r--test/Makefile.am18
-rw-r--r--test/eventcomm-test.c327
-rw-r--r--test/fake-symbols.c475
-rw-r--r--test/fake-symbols.h187
-rw-r--r--tools/.gitignore3
-rw-r--r--tools/Makefile.am31
-rw-r--r--tools/synclient.c645
-rw-r--r--tools/syndaemon.c651
-rw-r--r--xorg-synaptics.pc.in6
41 files changed, 13831 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c496323
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,78 @@
+#
+# X.Org module default exclusion patterns
+# The next section if for module specific patterns
+#
+# Do not edit the following section
+# GNU Build System (Autotools)
+aclocal.m4
+autom4te.cache/
+autoscan.log
+ChangeLog
+compile
+config.guess
+config.h
+config.h.in
+config.log
+config-ml.in
+config.py
+config.status
+config.status.lineno
+config.sub
+configure
+configure.scan
+depcomp
+.deps/
+INSTALL
+install-sh
+.libs/
+libtool
+libtool.m4
+ltmain.sh
+lt~obsolete.m4
+ltoptions.m4
+ltsugar.m4
+ltversion.m4
+Makefile
+Makefile.in
+mdate-sh
+missing
+mkinstalldirs
+*.pc
+py-compile
+stamp-h?
+symlink-tree
+texinfo.tex
+ylwrap
+
+# Do not edit the following section
+# Edit Compile Debug Document Distribute
+*~
+*.[0-9]
+*.[0-9]x
+*.bak
+*.bin
+core
+*.dll
+*.exe
+*-ISO*.bdf
+*-JIS*.bdf
+*-KOI8*.bdf
+*.kld
+*.ko
+*.ko.cmd
+*.lai
+*.l[oa]
+*.[oa]
+*.obj
+*.patch
+*.so
+*.pcf.gz
+*.pdb
+*.tar.bz2
+*.tar.gz
+#
+# Add & Override patterns for xf86-input-synaptics
+#
+# Edit the following section as needed
+# For example, !report.pc overrides *.pc. See 'man gitignore'
+#
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d6358be
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,40 @@
+The MIT License
+
+Copyright (c) 1997 C. Scott Ananian
+Copyright (c) 1998-2000 Bruce Kalk
+Copyright (c) 1999 Henry Davies
+Copyright (c) 2008 Fedor P. Goncharov
+Copyright (c) 2001 Stefan Gmeiner
+Copyright (c) 2002 S. Lehner
+Copyright (c) 2002 Linuxcare Inc. David Kennedy
+Copyright (c) 2003 Fred Hucht
+Copyright (c) 2003 Neil Brown
+Copyright (c) 2003 Jörg Bösner
+Copyright (c) 2003 Hartwig Felger
+Copyright (c) 2002-2007 Peter Osterlund
+Copyright (c) 2004 Arne Schwabe
+Copyright (c) 2004 Matthias Ihmig
+Copyright (c) 2004 Alexei Gilchrist
+Copyright (c) 2006-2007 Christian Thaeter
+Copyright (c) 2006 Stefan Bethge
+Copyright (c) 2007 Joseph P. Skudlarek
+Copyright (c) 2007 Florian Loitsch
+Copyright (c) 2008-2009 Red Hat, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..6fdd067
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,41 @@
+# 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.
+
+# During distcheck, system locations (as provided by pkg-config) may
+# not be writable; provide instead relative locations.
+DISTCHECK_CONFIGURE_FLAGS = \
+ --with-sdkdir='$${includedir}/xorg' \
+ --with-xorg-conf-dir='$${datadir}/X11/xorg.conf.d'
+
+SUBDIRS = include src man tools conf test
+MAINTAINERCLEANFILES = ChangeLog INSTALL
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = xorg-synaptics.pc
+
+.PHONY: ChangeLog INSTALL
+
+INSTALL:
+ $(INSTALL_CMD)
+
+ChangeLog:
+ $(CHANGELOG_CMD)
+
+dist-hook: ChangeLog INSTALL
diff --git a/README b/README
new file mode 100644
index 0000000..65f1999
--- /dev/null
+++ b/README
@@ -0,0 +1,152 @@
+Synaptics touchpad driver for X.Org
+-----------------------------------
+
+FAQ
+---
+
+* Is this free software?
+
+ Yes, the source code is released under the MIT license.
+
+* When will the driver be included in the XOrg distribution?
+
+ It is already.
+
+* How do I use this driver with Linux kernel 2.6.x?
+
+ You need to have the "evdev" driver loaded or compiled into the
+ kernel (CONFIG_INPUT_EVDEV). Set the "Protocol" parameter in the X
+ configuration file to "auto-dev". Also, if you set the "Device"
+ parameter to "/dev/psaux", the same X configuration file should
+ work for a 2.4.x kernel.
+
+ When configuring the kernel, enable PS/2 mouse support
+ (CONFIG_MOUSE_PS2).
+
+* It still doesn't work with a 2.6 kernel.
+
+ Some distributions come with an incomplete /dev directory. The
+ driver needs the /dev/input/eventX device nodes. Try to create
+ them manually if they don't exist already. (Look at
+ /proc/bus/input/devices to figure out how many nodes you need.)
+
+ # mknod /dev/input/event0 c 13 64
+ # mknod /dev/input/event1 c 13 65
+ # mknod /dev/input/event2 c 13 66
+ ...
+
+* How can I configure tap-to-click behavior?
+
+ If you set MaxTapTime=0 in the X config file then the touchpad
+ will not use tapping at all, i.e. touching/tapping will not be
+ taken as a mouse click.
+
+ If, instead, you set MaxTapMove=0 in the X config file, then the
+ touchpad will not use tapping for a single finger tap (left mouse
+ button click) but will for the two and three finger tap (middle
+ and right button click).
+
+* Why did tap-to-click stop working after I upgraded from an old version?
+
+ Time is now measured in milliseconds instead of "number of
+ packets". In practice, this means that if you are upgrading from
+ an old version, you need to change MaxTapTime and
+ EmulateMidButtonTime to make "tap to click" work. Good values are
+ 180 and 75 respectively.
+
+* Gnome scrollbars scroll too much when using tap-to-click. Why?
+
+ The ClickTime parameter is probably too big. Try setting it to
+ 100. Gnome scrollbars use auto repeat, ie if you press the left
+ mouse button and keep it pressed, the scroll bar will move until
+ you release the button. This will lead to problems if the tap time
+ is longer than the delay before auto repeat starts.
+
+* Vertical and horizontal scrolling events are mixed up. How come?
+
+ Probably because some X startup/login script uses xmodmap to remap
+ the mouse buttons. Correct settings for the touchpad are:
+
+ xmodmap -e 'pointer = 1 2 3 4 5 6 7'
+
+ You can check the current settings by running:
+
+ xmodmap -pp
+
+* Horizontal scrolling doesn't work in some programs. Is it a driver
+ bug?
+
+ No, probably not. Support for horizontal scroll events must be
+ handled by the application programs. Not all programs do that
+ yet. Ask the authors of the application in question to implement
+ support for horizontal scroll events.
+
+ You can use the "xev" program to check if the synaptics driver
+ generates the horizontal scroll events.
+
+ If you are having problems with Mozilla, try this link:
+
+ http://lists.debian.org/debian-laptop/2004/08/msg00167.html
+
+* Can the driver be used together with gpm?
+
+ No, not reliably, if you are using a 2.4.x kernel. The gpm driver
+ and the X driver both try to read data from the touchpad, and if
+ they try to read at the same time, both drivers see incomplete
+ data and don't know how to interpret it.
+
+ If you are running a 2.6.x kernel though, there should be no
+ conflict, because the kernel driver will make sure both user space
+ drivers receive all events from the touchpad.
+
+* Can I use this driver with an ALPS Glidepoint device?
+
+ Yes, see the README.alps file for more information.
+
+* The driver says "reset failed" and the touchpad doesn't work. What
+ can I do?
+
+ This problem has been reported for some Compaq models. It's
+ currently not known why it happens, but removing the reset command
+ from the driver appears to make it work. If you use a 2.4 linux
+ kernel, replace the contents of the ps2_synaptics_reset() function
+ in ps2comm.c with a "return TRUE;" statement. If you use a 2.6
+ linux kernel, remove the while loop in synaptics_query_hardware()
+ in the file drivers/input/mouse/synaptics.c in the linux kernel
+ source code.
+
+
+Authors
+-------
+
+Many people have contributed to this driver. Look at the top of
+synaptics.c and ps2comm.c for details.
+
+The current maintainer is X.org development team <xorg-devel@lists.x.org>.
+
+
+Contacts
+--------
+All questions regarding this software should be directed at the
+Xorg mailing list:
+
+ http://lists.freedesktop.org/mailman/listinfo/xorg
+
+Please submit bug reports to the Xorg bugzilla:
+
+ https://bugs.freedesktop.org/enter_bug.cgi?product=xorg
+
+The master development code repository can be found at:
+
+ git://anongit.freedesktop.org/git/xorg/driver/xf86-input-synaptics
+
+ http://cgit.freedesktop.org/xorg/driver/xf86-input-synaptics
+
+For patch submission instructions, see:
+
+ http://www.x.org/wiki/Development/Documentation/SubmittingPatches
+
+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..904cd67
--- /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/conf/11-x11-synaptics.fdi b/conf/11-x11-synaptics.fdi
new file mode 100644
index 0000000..d487f09
--- /dev/null
+++ b/conf/11-x11-synaptics.fdi
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- Example fdi file that assigns the synaptics driver to any touchpad in
+ the system.
+
+ DO NOT EDIT THIS FILE, your distribution will likely overwrite
+ it when updating. Copy (and rename) this file into
+ /etc/hal/fdi/policy first before adding options.
+ -->
+<deviceinfo version="0.2">
+ <device>
+ <match key="info.capabilities" contains="input.touchpad">
+ <merge key="input.x11_driver" type="string">synaptics</merge>
+ <!-- Arbitrary options can be passed to the driver using
+ the input.x11_options property since xorg-server-1.5. -->
+ <!-- EXAMPLES:
+ Maximum movement of the finger for detecting a tap
+ <merge key="input.x11_options.MaxTapMove" type="string">2000</merge>
+
+ Enable vertical scrolling when dragging along the right edge
+ <merge key="input.x11_options.VertEdgeScroll" type="string">true</merge>
+
+ Enable vertical scrolling when dragging with two fingers anywhere on the touchpad
+ <merge key="input.x11_options.VertTwoFingerScroll" type="string">true</merge>
+
+ Enable horizontal scrolling when dragging with two fingers anywhere on the touchpad
+ <merge key="input.x11_options.HorizTwoFingerScroll" type="string">true</merge>
+
+ If on, circular scrolling is used
+ <merge key="input.x11_options.CircularScrolling" type="string">true</merge>
+
+ For other possible options, check CONFIGURATION DETAILS in synaptics man page
+ -->
+ </match>
+ </device>
+</deviceinfo>
diff --git a/conf/50-synaptics.conf b/conf/50-synaptics.conf
new file mode 100644
index 0000000..9e86a7a
--- /dev/null
+++ b/conf/50-synaptics.conf
@@ -0,0 +1,44 @@
+# Example xorg.conf.d snippet that assigns the touchpad driver
+# to all touchpads. See xorg.conf.d(5) for more information on
+# InputClass.
+# DO NOT EDIT THIS FILE, your distribution will likely overwrite
+# it when updating. Copy (and rename) this file into
+# /etc/X11/xorg.conf.d first.
+# Additional options may be added in the form of
+# Option "OptionName" "value"
+#
+Section "InputClass"
+ Identifier "touchpad catchall"
+ Driver "synaptics"
+ MatchIsTouchpad "on"
+# This option is recommend on all Linux systems using evdev, but cannot be
+# enabled by default. See the following link for details:
+# http://who-t.blogspot.com/2010/11/how-to-ignore-configuration-errors.html
+# MatchDevicePath "/dev/input/event*"
+EndSection
+
+Section "InputClass"
+ Identifier "touchpad ignore duplicates"
+ MatchIsTouchpad "on"
+ MatchOS "Linux"
+ MatchDevicePath "/dev/input/mouse*"
+ Option "Ignore" "on"
+EndSection
+
+# This option enables the bottom right corner to be a right button on
+# non-synaptics clickpads.
+# This option is only interpreted by clickpads.
+Section "InputClass"
+ Identifier "Default clickpad buttons"
+ MatchDriver "synaptics"
+ Option "SoftButtonAreas" "50% 0 82% 0 0 0 0 0"
+EndSection
+
+# This option disables software buttons on Apple touchpads.
+# This option is only interpreted by clickpads.
+Section "InputClass"
+ Identifier "Disable clickpad buttons on Apple touchpads"
+ MatchProduct "Apple|bcm5974"
+ MatchDriver "synaptics"
+ Option "SoftButtonAreas" "0 0 0 0 0 0 0 0"
+EndSection
diff --git a/conf/Makefile.am b/conf/Makefile.am
new file mode 100644
index 0000000..38d2a01
--- /dev/null
+++ b/conf/Makefile.am
@@ -0,0 +1,27 @@
+# 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.
+
+
+if HAS_XORG_CONF_DIR
+dist_config_DATA = 50-synaptics.conf
+else
+fdidir = $(datadir)/hal/fdi/policy/20thirdparty
+dist_fdi_DATA = 11-x11-synaptics.fdi
+endif
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..894d4f5
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,176 @@
+# 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
+
+# Initialize Autoconf
+AC_PREREQ([2.60])
+AC_INIT([xf86-input-synaptics],
+ [1.6.2],
+ [https://bugs.freedesktop.org/enter_bug.cgi?product=xorg],
+ [xf86-input-synaptics])
+AC_CONFIG_SRCDIR([Makefile.am])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_AUX_DIR(.)
+
+# Initialize Automake
+AM_INIT_AUTOMAKE([foreign dist-bzip2])
+AM_MAINTAINER_MODE
+
+# Initialize libtool
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+
+# Initialize X.Org macros 1.13 or later for XORG_ENABLE_UNIT_TESTS
+m4_ifndef([XORG_MACROS_VERSION],
+ [m4_fatal([must install xorg-macros 1.13 or later before running autoconf/autogen])])
+XORG_MACROS_VERSION(1.13)
+XORG_DEFAULT_OPTIONS
+XORG_ENABLE_UNIT_TESTS
+
+# Checks for libraries.
+AC_CHECK_LIB([m], [rint])
+
+# Store the list of server defined optional extensions in REQUIRED_MODULES
+m4_ifndef([XORG_DRIVER_CHECK_EXT],
+ [m4_fatal([must install xorg-server development files before running autoconf/autogen])])
+XORG_DRIVER_CHECK_EXT(RANDR, randrproto)
+
+# Obtain compiler/linker options for the Synaptics driver dependencies
+PKG_CHECK_MODULES(XORG, [xorg-server >= 1.7] xproto inputproto $REQUIRED_MODULES)
+
+# X Server SDK location is required to install Synaptics header files
+# This location is also relayed in the xorg-synaptics.pc file
+sdkdir=`$PKG_CONFIG --variable=sdkdir xorg-server`
+AC_SUBST([sdkdir])
+
+DRIVER_NAME=synaptics
+AC_SUBST([DRIVER_NAME])
+
+PKG_CHECK_MODULES(XI22, [inputproto >= 2.1.99.3] [xorg-server >= 1.11.99.901], HAVE_XI22="yes", HAVE_XI22="no")
+if test "x$HAVE_XI22" = xyes; then
+ AC_DEFINE(HAVE_MULTITOUCH, 1, [XI2.2 available])
+fi
+
+# -----------------------------------------------------------------------------
+# Configuration options
+# -----------------------------------------------------------------------------
+# Define a configure option for an alternate input module directory
+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)
+
+# Define a configure option for an alternate X Server configuration directory
+sysconfigdir=`$PKG_CONFIG --variable=sysconfigdir xorg-server`
+AC_ARG_WITH(xorg-conf-dir,
+ AC_HELP_STRING([--with-xorg-conf-dir=DIR],
+ [Default xorg.conf.d directory [[default=from $PKG_CONFIG xorg-server]]]),
+ [configdir="$withval"],
+ [configdir="$sysconfigdir"])
+AC_SUBST(configdir)
+AM_CONDITIONAL(HAS_XORG_CONF_DIR, [test "x$sysconfigdir" != "x"])
+
+# Define a configure option to enable code debugging
+AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],
+ [Enable debugging (default: disabled)]),
+ [DEBUGGING=$enableval], [DEBUGGING=no])
+if test "x$DEBUGGING" = xyes; then
+ AC_DEFINE(DEBUG, 1, [Enable debugging code])
+fi
+AM_CONDITIONAL(DEBUG, [test "x$DEBUGGING" = xyes])
+
+# -----------------------------------------------------------------------------
+# Determine which backend, if any, to build
+# -----------------------------------------------------------------------------
+AC_MSG_CHECKING([which optional backends will be build])
+case "${host}" in
+*linux*)
+ AC_MSG_RESULT([ps2comm alpscomm eventcomm])
+ BUILD_PS2COMM="yes"
+ BUILD_EVENTCOMM="yes"
+ ;;
+*freebsd* | *netbsd* | *dragonfly*)
+ AC_MSG_RESULT([ps2comm alpscomm psmcomm])
+ BUILD_PS2COMM="yes"
+ BUILD_PSMCOMM="yes"
+ ;;
+*solaris*)
+ AC_MSG_RESULT([ps2comm alpscomm])
+ BUILD_PS2COMM="yes"
+ ;;
+*)
+ AC_MSG_ERROR([Cannot find suitable backends for this platform.])
+ ;;
+esac
+if test "x$BUILD_EVENTCOMM" = xyes; then
+ AC_DEFINE(BUILD_EVENTCOMM, 1, [Optional backend eventcomm enabled])
+
+ if test "x$HAVE_XI22" = xyes; then
+ # Obtain compiler/linker options for mtdev
+ PKG_CHECK_MODULES(MTDEV, mtdev)
+ fi
+fi
+if test "x$BUILD_PSMCOMM" = xyes; then
+ AC_DEFINE(BUILD_PSMCOMM, 1, [Optional backend psmcomm enabled])
+fi
+if test "x$BUILD_PS2COMM" = xyes; then
+ AC_DEFINE(BUILD_PS2COMM, 1, [Optional backend ps2comm and alpscomm enabled])
+fi
+AM_CONDITIONAL([BUILD_EVENTCOMM], [test "x${BUILD_EVENTCOMM}" = "xyes"])
+AM_CONDITIONAL([BUILD_PSMCOMM], [test "x${BUILD_PSMCOMM}" = "xyes"])
+AM_CONDITIONAL([BUILD_PS2COMM], [test "x${BUILD_PS2COMM}" = "xyes"])
+
+# -----------------------------------------------------------------------------
+# Dependencies for synclient and syndaemon
+# -----------------------------------------------------------------------------
+# Obtain compiler/linker options for the Synaptics apps dependencies
+PKG_CHECK_MODULES(XI, x11 inputproto [xi >= 1.2])
+
+# The syndaemon program uses an optional XRecord extension implementation
+# If libxtst >= 1.0.99 is installed, Cflags contains the path to record.h
+# If recordproto < 1.13.99.1 is installed, Cflags contains the path to record.h
+PKG_CHECK_MODULES(XTST, xtst recordproto, have_libxtst="yes", have_libxtst="no")
+if test "x$have_libxtst" = "xyes" ; then
+ # Header record.h may come from the xtst or recordproto package, or may be missing
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $XTST_CFLAGS"
+ AC_CHECK_HEADERS([X11/extensions/record.h],,,[#include <X11/Xlib.h>])
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+# -----------------------------------------------------------------------------
+
+# Workaround overriding sdkdir to be able to create a tarball when user has no
+# write permission in sdkdir. See DISTCHECK_CONFIGURE_FLAGS in Makefile.am
+AC_ARG_WITH([sdkdir], [], [sdkdir="$withval"])
+
+AC_CONFIG_FILES([Makefile
+ src/Makefile
+ man/Makefile
+ tools/Makefile
+ conf/Makefile
+ include/Makefile
+ test/Makefile
+ xorg-synaptics.pc])
+AC_OUTPUT
+
diff --git a/docs/README.alps b/docs/README.alps
new file mode 100644
index 0000000..d89b38b
--- /dev/null
+++ b/docs/README.alps
@@ -0,0 +1,77 @@
+It is possible to use this driver with an ALPS Glidepoint device. If
+you use an older 2.6 linux kernel which has no ALPS input driver, you
+need to apply the ALPS kernel patch in the alps.patch file. See
+http://www.kernelnewbies.org/faq/ for information about how to apply
+kernel patches and compile kernels.
+
+Note! If you use kernel 2.6.11 or later, the alps patch is already
+included, so you don't have to patch your kernel.
+
+Since ALPS touchpads don't have the same resolution as Synaptics
+touchpads, you probably have to change some parameter values. Here is
+an example InputDevice section for the X configuration file.
+
+Section "InputDevice"
+ Driver "synaptics"
+ Identifier "Mouse[1]"
+ Option "Device" "/dev/psaux"
+ Option "Protocol" "auto-dev"
+# enable SHMConfig if you want to enable synclient
+# NB: enabling SHMConfig is insecure, since any user can invoke it
+# Option "SHMConfig" "on"
+ Option "LeftEdge" "120"
+ Option "RightEdge" "830"
+ Option "TopEdge" "120"
+ Option "BottomEdge" "650"
+ Option "FingerLow" "14"
+ Option "FingerHigh" "15"
+ Option "MaxTapTime" "180"
+ Option "MaxTapMove" "110"
+ Option "EmulateMidButtonTime" "75"
+ Option "VertScrollDelta" "20"
+ Option "HorizScrollDelta" "20"
+ Option "CornerCoasting" "1"
+ Option "CoastingSpeed" "3"
+ Option "MinSpeed" "0.3"
+ Option "MaxSpeed" "0.75"
+ Option "AccelFactor" "0.015"
+ Option "EdgeMotionMinSpeed" "200"
+ Option "EdgeMotionMaxSpeed" "200"
+ Option "UpDownScrolling" "1"
+ Option "CircularScrolling" "1"
+ Option "CircScrollDelta" "0.1"
+ Option "CircScrollTrigger" "2"
+EndSection
+
+If you use a 2.4 linux kernel, you don't need to patch the kernel, but
+you should instead set "Protocol" like this:
+
+ Option "Protocol" "alps"
+
+
+On some (all?) ALPS hardware, it is not possible to disable tapping
+unless you apply the patch below. However, some users have reported
+that this patch breaks tap-and-drag operations, which is why the patch
+is not included in the main alps.patch file.
+
+--- linux/drivers/input/mouse/alps.c~alps-test3 2004-02-28 20:46:34.000000000 +0100
++++ linux-petero/drivers/input/mouse/alps.c 2004-02-28 20:49:12.000000000 +0100
+@@ -87,6 +87,10 @@ static void ALPS_process_packet(struct p
+ y = (packet[4] & 0x7f) | ((packet[3] & 0x70)<<(7-4));
+ z = packet[5];
+
++ if (packet[2] & 1) {
++ z = 35;
++ }
++
+ if (z > 0) {
+ input_report_abs(dev, ABS_X, x);
+ input_report_abs(dev, ABS_Y, y);
+@@ -97,7 +101,6 @@ static void ALPS_process_packet(struct p
+ if (z > 30) input_report_key(dev, BTN_TOUCH, 1);
+ if (z < 25) input_report_key(dev, BTN_TOUCH, 0);
+
+- left |= (packet[2] ) & 1;
+ left |= (packet[3] ) & 1;
+ right |= (packet[3] >> 1) & 1;
+ if (packet[0] == 0xff) {
diff --git a/docs/tapndrag.dia b/docs/tapndrag.dia
new file mode 100644
index 0000000..13c6108
--- /dev/null
+++ b/docs/tapndrag.dia
@@ -0,0 +1,1827 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/">
+ <dia:diagramdata>
+ <dia:attribute name="background">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="pagebreak">
+ <dia:color val="#000099"/>
+ </dia:attribute>
+ <dia:attribute name="paper">
+ <dia:composite type="paper">
+ <dia:attribute name="name">
+ <dia:string>#A4#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="tmargin">
+ <dia:real val="2.8222000598907471"/>
+ </dia:attribute>
+ <dia:attribute name="bmargin">
+ <dia:real val="2.8222000598907471"/>
+ </dia:attribute>
+ <dia:attribute name="lmargin">
+ <dia:real val="2.8222000598907471"/>
+ </dia:attribute>
+ <dia:attribute name="rmargin">
+ <dia:real val="2.8222000598907471"/>
+ </dia:attribute>
+ <dia:attribute name="is_portrait">
+ <dia:boolean val="true"/>
+ </dia:attribute>
+ <dia:attribute name="scaling">
+ <dia:real val="0.45899999141693115"/>
+ </dia:attribute>
+ <dia:attribute name="fitto">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="grid">
+ <dia:composite type="grid">
+ <dia:attribute name="width_x">
+ <dia:real val="1"/>
+ </dia:attribute>
+ <dia:attribute name="width_y">
+ <dia:real val="1"/>
+ </dia:attribute>
+ <dia:attribute name="visible_x">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:attribute name="visible_y">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:composite type="color"/>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#d8e5e5"/>
+ </dia:attribute>
+ <dia:attribute name="guides">
+ <dia:composite type="guides">
+ <dia:attribute name="hguides"/>
+ <dia:attribute name="vguides"/>
+ </dia:composite>
+ </dia:attribute>
+ </dia:diagramdata>
+ <dia:layer name="Background" visible="true">
+ <dia:object type="UML - Usecase" version="0" id="O0">
+ <dia:attribute name="obj_pos">
+ <dia:point val="10.3763,6.64119"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="10.3763,6.64119;13.6263,8.64119"/>
+ </dia:attribute>
+ <dia:attribute name="elem_corner">
+ <dia:point val="10.3763,6.64119"/>
+ </dia:attribute>
+ <dia:attribute name="elem_width">
+ <dia:real val="3.25"/>
+ </dia:attribute>
+ <dia:attribute name="elem_height">
+ <dia:real val="2"/>
+ </dia:attribute>
+ <dia:attribute name="line_colour">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="fill_colour">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="text_outside">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="collaboration">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#Start#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="arial" style="0" name="Helvetica"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="12.0013,7.77119"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="UML - Usecase" version="0" id="O1">
+ <dia:attribute name="obj_pos">
+ <dia:point val="10.3763,13.1485"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="10.3763,13.1485;13.6263,15.1485"/>
+ </dia:attribute>
+ <dia:attribute name="elem_corner">
+ <dia:point val="10.3763,13.1485"/>
+ </dia:attribute>
+ <dia:attribute name="elem_width">
+ <dia:real val="3.25"/>
+ </dia:attribute>
+ <dia:attribute name="elem_height">
+ <dia:real val="2"/>
+ </dia:attribute>
+ <dia:attribute name="line_colour">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="fill_colour">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="text_outside">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="collaboration">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#1#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="arial" style="0" name="Helvetica"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="12.0013,14.2785"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="UML - Usecase" version="0" id="O2">
+ <dia:attribute name="obj_pos">
+ <dia:point val="3.81628,19.5885"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="3.81628,19.5885;7.06628,21.5885"/>
+ </dia:attribute>
+ <dia:attribute name="elem_corner">
+ <dia:point val="3.81628,19.5885"/>
+ </dia:attribute>
+ <dia:attribute name="elem_width">
+ <dia:real val="3.25"/>
+ </dia:attribute>
+ <dia:attribute name="elem_height">
+ <dia:real val="2"/>
+ </dia:attribute>
+ <dia:attribute name="line_colour">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="fill_colour">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="text_outside">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="collaboration">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#2b#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="arial" style="0" name="Helvetica"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="5.44128,20.7185"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="UML - Usecase" version="0" id="O3">
+ <dia:attribute name="obj_pos">
+ <dia:point val="10.3763,26.743"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="10.3763,26.743;13.6263,28.743"/>
+ </dia:attribute>
+ <dia:attribute name="elem_corner">
+ <dia:point val="10.3763,26.743"/>
+ </dia:attribute>
+ <dia:attribute name="elem_width">
+ <dia:real val="3.25"/>
+ </dia:attribute>
+ <dia:attribute name="elem_height">
+ <dia:real val="2"/>
+ </dia:attribute>
+ <dia:attribute name="line_colour">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="fill_colour">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="text_outside">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="collaboration">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#3#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="arial" style="0" name="Helvetica"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="12.0013,27.873"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="UML - Usecase" version="0" id="O4">
+ <dia:attribute name="obj_pos">
+ <dia:point val="19.7687,13.1485"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="19.7687,13.1485;23.0187,15.1485"/>
+ </dia:attribute>
+ <dia:attribute name="elem_corner">
+ <dia:point val="19.7687,13.1485"/>
+ </dia:attribute>
+ <dia:attribute name="elem_width">
+ <dia:real val="3.25"/>
+ </dia:attribute>
+ <dia:attribute name="elem_height">
+ <dia:real val="2"/>
+ </dia:attribute>
+ <dia:attribute name="line_colour">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="fill_colour">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="text_outside">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="collaboration">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#Move#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="arial" style="0" name="Helvetica"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="21.3937,14.2785"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="UML - Usecase" version="0" id="O5">
+ <dia:attribute name="obj_pos">
+ <dia:point val="19.7687,26.743"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="19.7687,26.743;23.0187,28.743"/>
+ </dia:attribute>
+ <dia:attribute name="elem_corner">
+ <dia:point val="19.7687,26.743"/>
+ </dia:attribute>
+ <dia:attribute name="elem_width">
+ <dia:real val="3.25"/>
+ </dia:attribute>
+ <dia:attribute name="elem_height">
+ <dia:real val="2"/>
+ </dia:attribute>
+ <dia:attribute name="line_colour">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="fill_colour">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="text_outside">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="collaboration">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#Drag#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="arial" style="0" name="Helvetica"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="21.3937,27.873"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Line" version="0" id="O6">
+ <dia:attribute name="obj_pos">
+ <dia:point val="12.0013,8.64119"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="11.2013,8.59119;12.8013,13.1985"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="12.0013,8.64119"/>
+ <dia:point val="12.0013,13.1485"/>
+ </dia:attribute>
+ <dia:attribute name="numcp">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O0" connection="6"/>
+ <dia:connection handle="1" to="O1" connection="1"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Line" version="0" id="O7">
+ <dia:attribute name="obj_pos">
+ <dia:point val="12.0013,15.1485"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="11.2013,15.0985;12.8013,19.6885"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="12.0013,15.1485"/>
+ <dia:point val="12.0013,19.6385"/>
+ </dia:attribute>
+ <dia:attribute name="numcp">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O1" connection="6"/>
+ <dia:connection handle="1" to="O35" connection="1"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Line" version="0" id="O8">
+ <dia:attribute name="obj_pos">
+ <dia:point val="5.44128,21.5885"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="5.37057,21.5178;11.4551,27.6352"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="5.44128,21.5885"/>
+ <dia:point val="10.8523,27.0359"/>
+ </dia:attribute>
+ <dia:attribute name="numcp">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O2" connection="6"/>
+ <dia:connection handle="1" to="O3" connection="0"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Line" version="0" id="O9">
+ <dia:attribute name="obj_pos">
+ <dia:point val="13.6263,14.1485"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="13.5763,13.3485;19.8187,14.9485"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="13.6263,14.1485"/>
+ <dia:point val="19.7687,14.1485"/>
+ </dia:attribute>
+ <dia:attribute name="numcp">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O1" connection="4"/>
+ <dia:connection handle="1" to="O4" connection="3"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Arc" version="0" id="O10">
+ <dia:attribute name="obj_pos">
+ <dia:point val="21.3937,13.1485"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="12.7045,7.63181;21.4621,13.2169"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="21.3937,13.1485"/>
+ <dia:point val="13.1503,8.3483"/>
+ </dia:attribute>
+ <dia:attribute name="curve_distance">
+ <dia:real val="0.34190799999999999"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O4" connection="1"/>
+ <dia:connection handle="1" to="O0" connection="7"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - BezierLine" version="0" id="O11">
+ <dia:attribute name="obj_pos">
+ <dia:point val="22.5427,27.0359"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="13.4221,6.84668;26.342,27.1059"/>
+ </dia:attribute>
+ <dia:attribute name="bez_points">
+ <dia:point val="22.5427,27.0359"/>
+ <dia:point val="31.4003,15.2912"/>
+ <dia:point val="22.9859,9.4912"/>
+ <dia:point val="13.6263,7.64119"/>
+ </dia:attribute>
+ <dia:attribute name="corner_types">
+ <dia:enum val="0"/>
+ <dia:enum val="0"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O5" connection="2"/>
+ <dia:connection handle="3" to="O0" connection="4"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - BezierLine" version="0" id="O12">
+ <dia:attribute name="obj_pos">
+ <dia:point val="4.29223,19.8814"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="0.626041,6.83963;10.4778,19.9508"/>
+ </dia:attribute>
+ <dia:attribute name="bez_points">
+ <dia:point val="4.29223,19.8814"/>
+ <dia:point val="-3.17884,14.883"/>
+ <dia:point val="1.95138,8.18572"/>
+ <dia:point val="10.3763,7.64119"/>
+ </dia:attribute>
+ <dia:attribute name="corner_types">
+ <dia:enum val="0"/>
+ <dia:enum val="0"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O2" connection="0"/>
+ <dia:connection handle="3" to="O0" connection="3"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - BezierLine" version="0" id="O13">
+ <dia:attribute name="obj_pos">
+ <dia:point val="10.3763,27.743"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="3.43771,20.9027;10.4428,28.2834"/>
+ </dia:attribute>
+ <dia:attribute name="bez_points">
+ <dia:point val="10.3763,27.743"/>
+ <dia:point val="7.18118,29.2357"/>
+ <dia:point val="1.35186,27.3857"/>
+ <dia:point val="4.29223,21.2956"/>
+ </dia:attribute>
+ <dia:attribute name="corner_types">
+ <dia:enum val="0"/>
+ <dia:enum val="0"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O3" connection="3"/>
+ <dia:connection handle="3" to="O2" connection="5"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="UML - Note" version="0" id="O14">
+ <dia:attribute name="obj_pos">
+ <dia:point val="1.84118,30.7912"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="1.79118,30.7412;14.3412,42.1412"/>
+ </dia:attribute>
+ <dia:attribute name="elem_corner">
+ <dia:point val="1.84118,30.7912"/>
+ </dia:attribute>
+ <dia:attribute name="elem_width">
+ <dia:real val="12.450000000000001"/>
+ </dia:attribute>
+ <dia:attribute name="elem_height">
+ <dia:real val="11.300000000000001"/>
+ </dia:attribute>
+ <dia:attribute name="line_colour">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="fill_colour">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#T : Touch
+R : Release
+TO : Tap Timeout
+TO2: Mouse click timeout
+TO3: Double tap timeout
+TO4: Single tap timeout
+TO5: Locked drag timeout
+M : Finger movement
+[U]: Generate button up event
+[D]: Generate button down event
+L : Locked drags enabled
+F : Fast taps enabled
+TDG: Tap-and-drag gesture enabled#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="2.19118,31.9287"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="0"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O15">
+ <dia:attribute name="obj_pos">
+ <dia:point val="11.23,10.8357"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="11.055,10.3482;11.405,11.3257"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#T#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="11.23,10.8357"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O16">
+ <dia:attribute name="obj_pos">
+ <dia:point val="8.4912,23.1912"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="7.9662,22.7037;9.0162,24.4812"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#T
+[D]#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="8.4912,23.1912"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O17">
+ <dia:attribute name="obj_pos">
+ <dia:point val="17.0662,11.0357"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="16.8912,10.5482;17.2412,11.5257"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#R#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="17.0662,11.0357"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Line" version="0" id="O18">
+ <dia:attribute name="obj_pos">
+ <dia:point val="13.6263,27.743"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="13.5763,26.943;19.8187,28.543"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="13.6263,27.743"/>
+ <dia:point val="19.7687,27.743"/>
+ </dia:attribute>
+ <dia:attribute name="numcp">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O3" connection="4"/>
+ <dia:connection handle="1" to="O5" connection="3"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O19">
+ <dia:attribute name="obj_pos">
+ <dia:point val="13.73,17.0857"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="12.33,16.5982;15.13,18.3757"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#R
+[D] if F#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="13.73,17.0857"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O20">
+ <dia:attribute name="obj_pos">
+ <dia:point val="5.32686,26.1857"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="4.80186,25.6982;5.85186,27.4757"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#R
+[U]#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="5.32686,26.1857"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O21">
+ <dia:attribute name="obj_pos">
+ <dia:point val="16.3912,13.6912"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="15.1662,13.2037;17.6162,14.1812"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#TO or M#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="16.3912,13.6912"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O22">
+ <dia:attribute name="obj_pos">
+ <dia:point val="16.1,28.5375"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="14.525,28.0334;17.6916,29.8607"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#(TO or M)
+and TDG#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="16.1,28.5375"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O23">
+ <dia:attribute name="obj_pos">
+ <dia:point val="1.24118,9.9412"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="0.36618,9.4537;2.11618,11.2312"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#TO3
+[D,U]#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="1.24118,9.9412"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O24">
+ <dia:attribute name="obj_pos">
+ <dia:point val="24.4912,17.8412"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="23.4412,17.3537;25.5412,19.1312"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#R &amp; !L
+[U]#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="24.4912,17.8412"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="UML - Usecase" version="0" id="O25">
+ <dia:attribute name="obj_pos">
+ <dia:point val="19.7687,32.4912"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="19.7687,32.4912;23.0187,34.4912"/>
+ </dia:attribute>
+ <dia:attribute name="elem_corner">
+ <dia:point val="19.7687,32.4912"/>
+ </dia:attribute>
+ <dia:attribute name="elem_width">
+ <dia:real val="3.25"/>
+ </dia:attribute>
+ <dia:attribute name="elem_height">
+ <dia:real val="2"/>
+ </dia:attribute>
+ <dia:attribute name="line_colour">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="fill_colour">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="text_outside">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="collaboration">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#4#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="arial" style="0" name="Helvetica"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="21.3937,33.6212"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="UML - Usecase" version="0" id="O26">
+ <dia:attribute name="obj_pos">
+ <dia:point val="19.7687,38.0412"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="19.7687,38.0412;23.0187,40.0412"/>
+ </dia:attribute>
+ <dia:attribute name="elem_corner">
+ <dia:point val="19.7687,38.0412"/>
+ </dia:attribute>
+ <dia:attribute name="elem_width">
+ <dia:real val="3.25"/>
+ </dia:attribute>
+ <dia:attribute name="elem_height">
+ <dia:real val="2"/>
+ </dia:attribute>
+ <dia:attribute name="line_colour">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="fill_colour">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="text_outside">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="collaboration">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#5#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="arial" style="0" name="Helvetica"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="21.3937,39.1712"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Line" version="0" id="O27">
+ <dia:attribute name="obj_pos">
+ <dia:point val="21.3937,28.743"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="20.5937,28.693;22.1937,32.5412"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="21.3937,28.743"/>
+ <dia:point val="21.3937,32.4912"/>
+ </dia:attribute>
+ <dia:attribute name="numcp">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O5" connection="6"/>
+ <dia:connection handle="1" to="O25" connection="1"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Line" version="0" id="O28">
+ <dia:attribute name="obj_pos">
+ <dia:point val="21.3937,34.4912"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="20.5937,34.4412;22.1937,38.0912"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="21.3937,34.4912"/>
+ <dia:point val="21.3937,38.0412"/>
+ </dia:attribute>
+ <dia:attribute name="numcp">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O25" connection="6"/>
+ <dia:connection handle="1" to="O26" connection="1"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O29">
+ <dia:attribute name="obj_pos">
+ <dia:point val="20.15,30.7875"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="19.275,30.3;21.025,31.2775"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#R &amp; L#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="20.15,30.7875"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O30">
+ <dia:attribute name="obj_pos">
+ <dia:point val="20.7108,36.3412"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="20.5358,35.8537;20.8858,36.8312"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#T#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="20.7108,36.3412"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O31">
+ <dia:attribute name="obj_pos">
+ <dia:point val="16.7608,32.1912"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="16.4108,31.7037;17.1108,34.2812"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#TO
+or
+M#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="16.7608,32.1912"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - BezierLine" version="0" id="O32">
+ <dia:attribute name="obj_pos">
+ <dia:point val="20.2447,38.3341"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="17.4488,27.7324;20.6882,38.4036"/>
+ </dia:attribute>
+ <dia:attribute name="bez_points">
+ <dia:point val="20.2447,38.3341"/>
+ <dia:point val="17.1,36.1875"/>
+ <dia:point val="16.101,30.8446"/>
+ <dia:point val="20.2447,28.4501"/>
+ </dia:attribute>
+ <dia:attribute name="corner_types">
+ <dia:enum val="0"/>
+ <dia:enum val="0"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O26" connection="0"/>
+ <dia:connection handle="3" to="O5" connection="5"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - BezierLine" version="0" id="O33">
+ <dia:attribute name="obj_pos">
+ <dia:point val="23.0187,39.0412"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="11.7002,5.51197;30.2716,39.1017"/>
+ </dia:attribute>
+ <dia:attribute name="bez_points">
+ <dia:point val="23.0187,39.0412"/>
+ <dia:point val="32.7991,36.6412"/>
+ <dia:point val="35.6,-1.25"/>
+ <dia:point val="12.0013,6.64119"/>
+ </dia:attribute>
+ <dia:attribute name="corner_types">
+ <dia:enum val="0"/>
+ <dia:enum val="0"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O26" connection="4"/>
+ <dia:connection handle="3" to="O0" connection="1"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O34">
+ <dia:attribute name="obj_pos">
+ <dia:point val="31.1912,22.9412"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="30.6662,22.4537;31.7162,24.2312"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#R
+[U]#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="31.1912,22.9412"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="UML - Usecase" version="0" id="O35">
+ <dia:attribute name="obj_pos">
+ <dia:point val="10.3763,19.6385"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="10.3763,19.6385;13.6263,21.6385"/>
+ </dia:attribute>
+ <dia:attribute name="elem_corner">
+ <dia:point val="10.3763,19.6385"/>
+ </dia:attribute>
+ <dia:attribute name="elem_width">
+ <dia:real val="3.25"/>
+ </dia:attribute>
+ <dia:attribute name="elem_height">
+ <dia:real val="2"/>
+ </dia:attribute>
+ <dia:attribute name="line_colour">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="fill_colour">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="text_outside">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="collaboration">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#2a#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="arial" style="0" name="Helvetica"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="12.0013,20.7685"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="UML - Usecase" version="0" id="O36">
+ <dia:attribute name="obj_pos">
+ <dia:point val="3.36628,12.7652"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="3.36628,12.7652;7.39128,15.4485"/>
+ </dia:attribute>
+ <dia:attribute name="elem_corner">
+ <dia:point val="3.36628,12.7652"/>
+ </dia:attribute>
+ <dia:attribute name="elem_width">
+ <dia:real val="4.0250000000000004"/>
+ </dia:attribute>
+ <dia:attribute name="elem_height">
+ <dia:real val="2.6833333333333336"/>
+ </dia:attribute>
+ <dia:attribute name="line_colour">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="fill_colour">
+ <dia:color val="#ffffff"/>
+ </dia:attribute>
+ <dia:attribute name="text_outside">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="collaboration">
+ <dia:boolean val="false"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#Single
+Tap#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="arial" style="0" name="Helvetica"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="5.37878,13.8369"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O37">
+ <dia:attribute name="obj_pos">
+ <dia:point val="8.11628,16.7357"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="6.54128,16.2482;9.69128,18.8257"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#TO4
+[U] if F
+[D] if !F#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="8.11628,16.7357"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Line" version="0" id="O38">
+ <dia:attribute name="obj_pos">
+ <dia:point val="12.0013,21.6385"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="11.2013,21.5885;12.8013,26.793"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="12.0013,21.6385"/>
+ <dia:point val="12.0013,26.743"/>
+ </dia:attribute>
+ <dia:attribute name="numcp">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O35" connection="6"/>
+ <dia:connection handle="1" to="O3" connection="1"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O39">
+ <dia:attribute name="obj_pos">
+ <dia:point val="13.8907,23.2857"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="12.1407,22.7816;15.6573,24.6089"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#T
+[D] if TDG#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="13.8907,23.2857"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Line" version="0" id="O40">
+ <dia:attribute name="obj_pos">
+ <dia:point val="5.44128,12.7652"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="5.37093,7.69694;11.3969,12.8356"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="5.44128,12.7652"/>
+ <dia:point val="10.8523,8.3483"/>
+ </dia:attribute>
+ <dia:attribute name="numcp">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O36" connection="1"/>
+ <dia:connection handle="1" to="O0" connection="5"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O41">
+ <dia:attribute name="obj_pos">
+ <dia:point val="7.37686,9.76072"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="6.85186,9.27322;7.90186,11.0507"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#TO2
+[U]#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="7.37686,9.76072"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Line" version="0" id="O42">
+ <dia:attribute name="obj_pos">
+ <dia:point val="7.51628,14.1485"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="7.46628,13.3485;10.4263,14.9485"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="7.51628,14.1485"/>
+ <dia:point val="10.3763,14.1485"/>
+ </dia:attribute>
+ <dia:attribute name="numcp">
+ <dia:int val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O36" connection="4"/>
+ <dia:connection handle="1" to="O1" connection="3"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O43">
+ <dia:attribute name="obj_pos">
+ <dia:point val="8.77686,12.9857"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="8.25186,12.4982;9.30186,14.2757"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#T
+[U]#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="8.77686,12.9857"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Arc" version="0" id="O44">
+ <dia:attribute name="obj_pos">
+ <dia:point val="10.3763,20.6385"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="4.60303,14.6434;10.447,20.7092"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="10.3763,20.6385"/>
+ <dia:point val="5.21628,15.2319"/>
+ </dia:attribute>
+ <dia:attribute name="curve_distance">
+ <dia:real val="-1.31629"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O35" connection="3"/>
+ <dia:connection handle="1" to="O36" connection="6"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - BezierLine" version="0" id="O45">
+ <dia:attribute name="obj_pos">
+ <dia:point val="22.5427,32.7841"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="13.0488,6.13252;28.0767,32.8535"/>
+ </dia:attribute>
+ <dia:attribute name="bez_points">
+ <dia:point val="22.5427,32.7841"/>
+ <dia:point val="34.574,14.9804"/>
+ <dia:point val="24.7,6.1875"/>
+ <dia:point val="13.1503,6.93408"/>
+ </dia:attribute>
+ <dia:attribute name="corner_types">
+ <dia:enum val="0"/>
+ <dia:enum val="0"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O25" connection="2"/>
+ <dia:connection handle="3" to="O0" connection="2"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O46">
+ <dia:attribute name="obj_pos">
+ <dia:point val="26.2,27.525"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="26.2,26.9825;27.3875,27.9225"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#TO5#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="sans" style="0" name="Helvetica"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="26.2,27.525"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="0"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ <dia:object type="Standard - Arc" version="0" id="O47">
+ <dia:attribute name="obj_pos">
+ <dia:point val="13.1503,27.0359"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="12.6503,14.8056;17.8,27.0859"/>
+ </dia:attribute>
+ <dia:attribute name="conn_endpoints">
+ <dia:point val="13.1503,27.0359"/>
+ <dia:point val="13.1503,14.8556"/>
+ </dia:attribute>
+ <dia:attribute name="curve_distance">
+ <dia:real val="4.5996514805718602"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow">
+ <dia:enum val="22"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_length">
+ <dia:real val="0.5"/>
+ </dia:attribute>
+ <dia:attribute name="end_arrow_width">
+ <dia:real val="0.5"/>
+ </dia:attribute>
+ <dia:connections>
+ <dia:connection handle="0" to="O3" connection="2"/>
+ <dia:connection handle="1" to="O1" connection="7"/>
+ </dia:connections>
+ </dia:object>
+ <dia:object type="Standard - Text" version="1" id="O48">
+ <dia:attribute name="obj_pos">
+ <dia:point val="19.5,20.7875"/>
+ </dia:attribute>
+ <dia:attribute name="obj_bb">
+ <dia:rectangle val="17.925,20.2834;21.0916,22.1107"/>
+ </dia:attribute>
+ <dia:attribute name="text">
+ <dia:composite type="text">
+ <dia:attribute name="string">
+ <dia:string>#(TO or M)
+and !TDG#</dia:string>
+ </dia:attribute>
+ <dia:attribute name="font">
+ <dia:font family="courier new" style="0" name="Courier"/>
+ </dia:attribute>
+ <dia:attribute name="height">
+ <dia:real val="0.8"/>
+ </dia:attribute>
+ <dia:attribute name="pos">
+ <dia:point val="19.5,20.7875"/>
+ </dia:attribute>
+ <dia:attribute name="color">
+ <dia:color val="#000000"/>
+ </dia:attribute>
+ <dia:attribute name="alignment">
+ <dia:enum val="1"/>
+ </dia:attribute>
+ </dia:composite>
+ </dia:attribute>
+ <dia:attribute name="valign">
+ <dia:enum val="3"/>
+ </dia:attribute>
+ </dia:object>
+ </dia:layer>
+</dia:diagram>
diff --git a/docs/trouble-shooting.txt b/docs/trouble-shooting.txt
new file mode 100644
index 0000000..3d2e012
--- /dev/null
+++ b/docs/trouble-shooting.txt
@@ -0,0 +1,152 @@
+Trouble-shooting guide
+----------------------
+
+Contents
+--------
+
+1. Check that the touchpad is correctly detected by the kernel
+2. Check that an external PS/2 mouse isn't causing problems
+3. Check if some other program is using the /dev/psaux device
+4. Check that the evdev kernel driver is available
+5. Check that the synaptics driver is properly loaded by the X server
+6. Check that the touchpad is enabled in the BIOS/hardware
+
+
+1. Check that the touchpad is correctly detected by the kernel
+--------------------------------------------------------------
+
+If you are using a 2.6 linux kernel, check the /proc/bus/input/devices
+file. The touchpad must be identified a "SynPS/2 Synaptics TouchPad"
+or an "AlpsPS/2 ALPS TouchPad". If it is identified as a "PS/2 Generic
+Mouse" or "PS/2 Synaptics TouchPad", something is wrong.
+
+Possible fixes:
+
+1. Check your BIOS settings. Some BIOSes can do USB -> PS/2 mouse
+ emulation which can interfere with the touchpad. There may be a way
+ to disable the legacy mouse emulation from the BIOS setup program.
+
+2. Arrange so that the kernel initializes the USB subsystem before the
+ PS/2 touchpad. Initializing the USB mouse sometimes disables the
+ BIOS emulation. Compiling psmouse as a module and loading it in
+ /etc/rc.d/rc.local usually assures the USB is initialized first.
+
+3. Disconnect the USB mouse and restart the computer. (Not really a fix,
+ but can help when trying to figure out what's wrong.)
+
+4. Make sure your boot loader doesn't pass any parameter to the kernel
+ that disables mouse extensions. ("psmouse_proto=bare" for example).
+ Alternatively, if psmouse is compiled as a module, make sure that
+ modprobe doesn't pass such parameters. Check /etc/modprobe.conf and
+ "rmmod psmouse; modprobe -v psmouse".
+
+If you run a 2.4 kernel or an non-linux kernel, the
+/proc/bus/input/devices file is not available, but the BIOS setting
+could be relevant anyway.
+
+
+2. Check that an external PS/2 mouse isn't causing problems
+-----------------------------------------------------------
+
+If you want to use an external PS/2 mouse at the same time as the
+synaptics touchpad driver, you must use a 2.6 linux kernel and your
+hardware (keyboard controller) must support active multiplexing. You
+should see something like this when the computer boots:
+
+ mice: PS/2 mouse device common for all mice
+ i8042.c: Detected active multiplexing controller, rev 1.1.
+ serio: i8042 AUX0 port at 0x60,0x64 irq 12
+ serio: i8042 AUX1 port at 0x60,0x64 irq 12
+ serio: i8042 AUX2 port at 0x60,0x64 irq 12
+ serio: i8042 AUX3 port at 0x60,0x64 irq 12
+
+If you don't use a 2.6 kernel or your hardware doesn't support active
+multiplexing, you can't use an external PS/2 mouse together with the
+touchpad driver.
+
+
+3. Check if some other program is using the /dev/psaux device
+-------------------------------------------------------------
+
+If you use a 2.4 linux kernel, only one program at a time can reliably
+read from /dev/psaux. This means that if you for example have GPM
+running, it will probably prevent the synaptics driver from working
+correctly. It also means that if you have a second InputDevice in your
+X configuration file, it must not read from /dev/psaux. You probably
+want it to read from /dev/input/mice instead, which will handle USB
+mice in both 2.4 and 2.6 linux kernels, and both USB and external PS/2
+mice if you use a 2.6 kernel.
+
+The 2.6 linux kernel fixes the /dev/psaux shortcoming, so that you can
+safely run GPM and the synaptics driver at the same time.
+
+
+4. Check that the evdev kernel driver is available
+--------------------------------------------------
+
+If you are using a 2.6 linux kernel, the evdev kernel driver is needed
+for the X driver to be able to communicate with the kernel driver.
+Check the /proc/bus/input/devices file. The Handlers= line should
+contain an event device name, like this:
+
+ H: Handlers=mouse0 event0
+
+If there is no event handler, you either have to load the evdev kernel
+module or recompile the kernel and build it into the kernel. If you
+don't want to recompile the kernel, adding "/sbin/modprobe evdev" to
+/etc/rc.d/rc.sysinit usually works.
+
+
+5. Check that the synaptics driver is properly loaded by the X server
+---------------------------------------------------------------------
+
+The X log file is usually called /var/log/XFree86.0.log or
+/var/log/Xorg.0.log. It should contain something like this:
+
+ (II) LoadModule: "synaptics"
+ (II) Loading /usr/X11R6/lib/modules/input/synaptics_drv.o
+ (II) Module synaptics: vendor="X.Org Foundation"
+ compiled for 4.3.99.902, module version = 1.0.0
+ Module class: X.Org XInput Driver
+ ABI class: X.Org XInput driver, version 0.4
+
+If the LoadModule line is missing, you probably forgot to add
+
+ Load "synaptics"
+
+to the "Module" section in the X config file, or you modified the
+wrong config file. Some systems that have been upgraded from XFree86
+to Xorg or from XFree86 3.x to XFree86 4.x can have multiple config
+files in the /etc/X11/ directory, but only one is used.
+
+Next, check that the log file also contains a line like this:
+
+ (II) Synaptics touchpad driver version 0.13.4
+
+If there is no such line, there is probably a binary compatibility
+problem between the synaptics driver and the X server.
+
+Possible fixes:
+
+1. Try upgrading to the latest synaptics driver.
+
+2. Try installing the X SDK package if it is available for your
+ version of X. (In Fedora Core 2, that package is called
+ xorg-x11-sdk-6.7.0-2.i386.rpm.) Then re-compile the synaptics
+ driver and try again.
+
+3. Make sure the driver is compiled with the same compiler version as
+ the X server.
+
+
+6. Check that the touchpad is enabled in the BIOS/hardware
+----------------------------------------------------------
+
+On some computers, it is possible to disable the touchpad either with
+a special key combination, from the BIOS, or with a special touchpad
+on/off button. On some machines, cycling the power doesn't
+automatically reenable the touchpad.
+
+If the touchpad appears to be dead, try to enable it from the BIOS or
+using a key combination. One user also reported that he had to remove
+the computer battery to make his touchpad operational again.
diff --git a/include/Makefile.am b/include/Makefile.am
new file mode 100644
index 0000000..f078e5e
--- /dev/null
+++ b/include/Makefile.am
@@ -0,0 +1,21 @@
+# 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.
+
+sdk_HEADERS = synaptics.h synaptics-properties.h
diff --git a/include/synaptics-properties.h b/include/synaptics-properties.h
new file mode 100644
index 0000000..ad7a502
--- /dev/null
+++ b/include/synaptics-properties.h
@@ -0,0 +1,167 @@
+/*
+ * 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
+ */
+
+#ifndef _SYNAPTICS_PROPERTIES_H_
+#define _SYNAPTICS_PROPERTIES_H_
+
+/**
+ * Properties exported by the synaptics driver. These properties are
+ * recognized by the driver and will change its behavior when modified.
+ * For a description of what each property does, see synaptics.h.
+ */
+
+/* 32 bit, 4 values, left, right, top, bottom */
+#define SYNAPTICS_PROP_EDGES "Synaptics Edges"
+
+/* 32 bit, 3 values, low, high, press */
+#define SYNAPTICS_PROP_FINGER "Synaptics Finger"
+
+/* 32 bit */
+#define SYNAPTICS_PROP_TAP_TIME "Synaptics Tap Time"
+
+/* 32 bit */
+#define SYNAPTICS_PROP_TAP_MOVE "Synaptics Tap Move"
+
+/* 32 bit, 3 values, single touch timeout, max tapping time for double
+ * taps, duration of a single click */
+#define SYNAPTICS_PROP_TAP_DURATIONS "Synaptics Tap Durations"
+
+/* 8 bit (BOOL) */
+#define SYNAPTICS_PROP_CLICKPAD "Synaptics ClickPad"
+
+/* 8 bit (BOOL) */
+#define SYNAPTICS_PROP_TAP_FAST "Synaptics Tap FastTap"
+
+/* 32 bit */
+#define SYNAPTICS_PROP_MIDDLE_TIMEOUT "Synaptics Middle Button Timeout"
+
+/* 32 bit */
+#define SYNAPTICS_PROP_TWOFINGER_PRESSURE "Synaptics Two-Finger Pressure"
+
+/* 32 bit */
+#define SYNAPTICS_PROP_TWOFINGER_WIDTH "Synaptics Two-Finger Width"
+
+/* 32 bit, 2 values, vert, horiz */
+#define SYNAPTICS_PROP_SCROLL_DISTANCE "Synaptics Scrolling Distance"
+
+/* 8 bit (BOOL), 3 values, vertical, horizontal, corner */
+#define SYNAPTICS_PROP_SCROLL_EDGE "Synaptics Edge Scrolling"
+
+/* 8 bit (BOOL), 2 values, vertical, horizontal */
+#define SYNAPTICS_PROP_SCROLL_TWOFINGER "Synaptics Two-Finger Scrolling"
+
+/* FLOAT, 4 values, min, max, accel, trackstick */
+#define SYNAPTICS_PROP_SPEED "Synaptics Move Speed"
+
+/* 32 bit, 2 values, min, max */
+#define SYNAPTICS_PROP_EDGEMOTION_PRESSURE "Synaptics Edge Motion Pressure"
+
+/* 32 bit, 2 values, min, max */
+#define SYNAPTICS_PROP_EDGEMOTION_SPEED "Synaptics Edge Motion Speed"
+
+/* 8 bit (BOOL) */
+#define SYNAPTICS_PROP_EDGEMOTION "Synaptics Edge Motion Always"
+
+/* 8 bit (BOOL), 2 values, updown, leftright */
+#define SYNAPTICS_PROP_BUTTONSCROLLING "Synaptics Button Scrolling"
+
+/* 8 bit (BOOL), 2 values, updown, leftright */
+#define SYNAPTICS_PROP_BUTTONSCROLLING_REPEAT "Synaptics Button Scrolling Repeat"
+
+/* 32 bit */
+#define SYNAPTICS_PROP_BUTTONSCROLLING_TIME "Synaptics Button Scrolling Time"
+
+/* 8 bit, valid values (0, 1, 2) */
+#define SYNAPTICS_PROP_OFF "Synaptics Off"
+
+/* 8 bit (BOOL) */
+#define SYNAPTICS_PROP_GUESTMOUSE "Synaptics Guestmouse Off"
+
+/* 8 bit (BOOL) */
+#define SYNAPTICS_PROP_LOCKED_DRAGS "Synaptics Locked Drags"
+
+/* 32 bit */
+#define SYNAPTICS_PROP_LOCKED_DRAGS_TIMEOUT "Synaptics Locked Drags Timeout"
+
+/* 8 bit, up to MAX_TAP values (see synaptics.h), 0 disables an
+ * element. order: RT, RB, LT, LB, F1, F2, F3 */
+#define SYNAPTICS_PROP_TAP_ACTION "Synaptics Tap Action"
+
+/* 8 bit, up to MAX_CLICK values (see synaptics.h), 0 disables an
+ * element. order: Finger 1, 2, 3 */
+#define SYNAPTICS_PROP_CLICK_ACTION "Synaptics Click Action"
+
+/* 8 bit (BOOL) */
+#define SYNAPTICS_PROP_CIRCULAR_SCROLLING "Synaptics Circular Scrolling"
+
+/* FLOAT */
+#define SYNAPTICS_PROP_CIRCULAR_SCROLLING_DIST "Synaptics Circular Scrolling Distance"
+
+/* 8 bit, valid values 0..8 (inclusive)
+ * order: any edge, top, top + right, right, right + bottom, bottom, bottom +
+ * left, left, left + top */
+#define SYNAPTICS_PROP_CIRCULAR_SCROLLING_TRIGGER "Synaptics Circular Scrolling Trigger"
+
+/* 8 bit (BOOL) */
+#define SYNAPTICS_PROP_CIRCULAR_PAD "Synaptics Circular Pad"
+
+/* 8 bit (BOOL) */
+#define SYNAPTICS_PROP_PALM_DETECT "Synaptics Palm Detection"
+
+/* 32 bit, 2 values, width, z */
+#define SYNAPTICS_PROP_PALM_DIMENSIONS "Synaptics Palm Dimensions"
+
+/* FLOAT, 2 values, speed, friction */
+#define SYNAPTICS_PROP_COASTING_SPEED "Synaptics Coasting Speed"
+
+/* CARD32, 2 values, min, max */
+#define SYNAPTICS_PROP_PRESSURE_MOTION "Synaptics Pressure Motion"
+
+/* FLOAT, 2 values, min, max */
+#define SYNAPTICS_PROP_PRESSURE_MOTION_FACTOR "Synaptics Pressure Motion Factor"
+
+/* 8 bit (BOOL) */
+#define SYNAPTICS_PROP_GRAB "Synaptics Grab Event Device"
+
+/* 8 bit (BOOL), 1 value, tap-and-drag */
+#define SYNAPTICS_PROP_GESTURES "Synaptics Gestures"
+
+/* 8 bit (BOOL), 7 values (read-only), has_left, has_middle, has_right,
+ * has_double, has_triple, has_pressure, has_width */
+#define SYNAPTICS_PROP_CAPABILITIES "Synaptics Capabilities"
+
+/* 32 bit unsigned, 2 values, vertical, horizontal in units/millimeter */
+#define SYNAPTICS_PROP_RESOLUTION "Synaptics Pad Resolution"
+
+/* 32 bit, 4 values, left, right, top, bottom */
+#define SYNAPTICS_PROP_AREA "Synaptics Area"
+
+/* 32 bit, 4 values, left, right, top, buttom */
+#define SYNAPTICS_PROP_SOFTBUTTON_AREAS "Synaptics Soft Button Areas"
+
+/* 32 Bit Integer, 2 values, horizontal hysteresis, vertical hysteresis */
+#define SYNAPTICS_PROP_NOISE_CANCELLATION "Synaptics Noise Cancellation"
+
+#endif /* _SYNAPTICS_PROPERTIES_H_ */
diff --git a/include/synaptics.h b/include/synaptics.h
new file mode 100644
index 0000000..3d4832f
--- /dev/null
+++ b/include/synaptics.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYNAPTICS_H_
+#define _SYNAPTICS_H_
+
+#include <X11/Xdefs.h>
+
+/******************************************************************************
+ * Public definitions.
+ * Used by driver and the shared memory configurator
+ *****************************************************************************/
+typedef enum {
+ RT_TAP = 0, /* Right top corner */
+ RB_TAP, /* Right bottom corner */
+ LT_TAP, /* Left top corner */
+ LB_TAP, /* Left bottom corner */
+ F1_TAP, /* Non-corner tap, one finger */
+ F2_TAP, /* Non-corner tap, two fingers */
+ F3_TAP, /* Non-corner tap, three fingers */
+ MAX_TAP
+} TapEvent;
+
+typedef enum {
+ F1_CLICK1 = 0, /* Click left, one finger */
+ F2_CLICK1, /* Click left, two fingers */
+ F3_CLICK1, /* Click left, three fingers */
+ MAX_CLICK
+} ClickFingerEvent;
+
+#define SYN_MAX_BUTTONS 12 /* Max number of mouse buttons */
+
+#define SHM_SYNAPTICS 23947
+typedef struct _SynapticsSHM {
+ int version; /* Driver version */
+
+ /* Current device state */
+ int x, y; /* actual x, y coordinates */
+ int z; /* pressure value */
+ int numFingers; /* number of fingers */
+ int fingerWidth; /* finger width value */
+ int left, right, up, down; /* left/right/up/down buttons */
+ Bool multi[8];
+ Bool middle;
+} SynapticsSHM;
+
+/*
+ * Minimum and maximum values for scroll_button_repeat
+ */
+#define SBR_MIN 10
+#define SBR_MAX 1000
+
+#endif /* _SYNAPTICS_H_ */
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644
index 0000000..8d56fe0
--- /dev/null
+++ b/man/Makefile.am
@@ -0,0 +1,47 @@
+# $Id$
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+#
+
+synclientmandir = $(APP_MAN_DIR)
+synclientman_PRE = synclient.man
+synclientman_DATA = $(synclientman_PRE:man=@APP_MAN_SUFFIX@)
+
+syndaemonmandir = $(APP_MAN_DIR)
+syndaemonman_PRE = syndaemon.man
+syndaemonman_DATA =$(syndaemonman_PRE:man=@APP_MAN_SUFFIX@)
+
+drivermandir = $(DRIVER_MAN_DIR)
+driverman_PRE = @DRIVER_NAME@.man
+driverman_DATA = $(driverman_PRE:man=@DRIVER_MAN_SUFFIX@)
+
+EXTRA_DIST = @DRIVER_NAME@.man synclient.man syndaemon.man
+
+CLEANFILES = $(driverman_DATA) $(synclientman_DATA) $(syndaemonman_DATA)
+
+SUFFIXES = .$(DRIVER_MAN_SUFFIX) .man
+
+# String replacements in MAN_SUBSTS now come from xorg-macros.m4 via configure
+.man.$(DRIVER_MAN_SUFFIX):
+ $(AM_V_GEN)$(SED) $(MAN_SUBSTS) < $< > $@
+.man.$(APP_MAN_SUFFIX):
+ $(AM_V_GEN)$(SED) $(MAN_SUBSTS) < $< > $@
diff --git a/man/synaptics.man b/man/synaptics.man
new file mode 100644
index 0000000..ab6e61d
--- /dev/null
+++ b/man/synaptics.man
@@ -0,0 +1,1029 @@
+.\" shorthand for double quote that works everywhere.
+.ds q \N'34'
+.TH SYNAPTICS __drivermansuffix__ __vendorversion__
+.SH NAME
+synaptics \- touchpad input driver
+.SH SYNOPSIS
+.nf
+.B "Section \*qInputDevice\*q"
+.BI " Identifier \*q" devname \*q
+.B " Driver \*qsynaptics\*q"
+.BI " Option \*qDevice\*q \*q" devpath \*q
+.BI " Option \*qPath\*q \*q" path \*q
+\ \ ...
+.B EndSection
+.fi
+.SH DESCRIPTION
+.B synaptics
+is an __xservername__ input driver for touchpads.
+Even though touchpads can be handled by the normal evdev or mouse drivers,
+this driver allows more advanced features of the
+touchpad to become available. Some benefits would be:
+.IP \(bu 4
+Movement with adjustable, non-linear acceleration and speed.
+.IP \(bu 4
+Button events through short touching of the touchpad.
+.IP \(bu 4
+Double-Button events through double short touching of the touchpad.
+.IP \(bu 4
+Dragging through short touching and holding down the finger on the
+touchpad (tap-and-drag gesture).
+.IP \(bu 4
+Middle and right button events on the upper and lower corner of the
+touchpad.
+.IP \(bu 4
+Vertical scrolling (button four and five events) through moving the
+finger on the right side of the touchpad.
+.IP \(bu 4
+The up/down button sends button four/five events.
+.IP \(bu 4
+Horizontal scrolling (button six and seven events) through moving the
+finger on the lower side of the touchpad.
+.IP \(bu 4
+The multi-buttons send button four/five events for vertical scrolling
+and button six/seven events for horizontal scrolling.
+.IP \(bu 4
+Adjustable finger detection.
+.IP \(bu 4
+Multifinger taps: two finger for right button and three finger for
+middle button events.
+.
+(Needs hardware support.
+.
+Not all models implement this feature.)
+.IP \(bu 4
+Pressure-dependent motion speed.
+.LP
+Note that depending on the touchpad firmware, some of these features
+might be available even without using the synaptics driver. Note also
+that some functions are not available on all touchpad models, because
+they need support from the touchpad hardware/firmware. (Multifinger
+taps for example.)
+.PP
+The name "synaptics" is historical and the driver still provides the
+synaptics protocol parsing code. Under Linux however, the hardware-specifics
+are handled by the kernel and this driver will work for any touchpad that
+has a working kernel driver. If your device is recognized as \*qPS/2
+Mouse\*q or similar, the kernel driver does not support your device and this
+driver will only provide limited functionality.
+
+.SH CONFIGURATION OPTIONS
+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 \*qDevice\*q \*q" string \*q
+This option specifies the device file in your \*q/dev\*q directory which will
+be used to access the physical device. Normally you should use something like
+\*q/dev/input/eventX\*q, where X is some integer.
+.TP 7
+.BI "Option \*qProtocol\*q \*q" string \*q
+Specifies which kernel driver will be used by this driver. This is the list of
+supported drivers and their default use scenarios.
+.TS
+l l.
+auto-dev automatic, default (recommend)
+event Linux 2.6 kernel events
+psaux raw device access (Linux 2.4)
+psm FreeBSD psm driver
+.TE
+.TP 7
+.BI "Option \*qSHMConfig\*q \*q" boolean \*q
+Switch on/off shared memory for run-time debugging. This option does not
+have an effect on run-time configuration anymore and is only useful for
+hardware event debugging.
+.TP 7
+.BI "Option \*qLeftEdge\*q \*q" integer \*q
+X coordinate for left edge. Property: "Synaptics Edges"
+.TP 7
+.BI "Option \*qRightEdge\*q \*q" integer \*q
+X coordinate for right edge. Property: "Synaptics Edges"
+.TP 7
+.BI "Option \*qTopEdge\*q \*q" integer \*q
+Y coordinate for top edge. Property: "Synaptics Edges"
+.TP 7
+.BI "Option \*qBottomEdge\*q \*q" integer \*q
+Y coordinate for bottom edge. Property: "Synaptics Edges"
+.TP 7
+.BI "Option \*qFingerLow\*q \*q" integer \*q
+When finger pressure drops below this value, the driver counts it as a
+release. Property: "Synaptics Finger"
+.TP 7
+.BI "Option \*qFingerHigh\*q \*q" integer \*q
+When finger pressure goes above this value, the driver counts it as a
+touch. Property: "Synaptics Finger"
+.TP 7
+.BI "Option \*qFingerPress\*q \*q" integer \*q
+When finger pressure goes above this value, the driver counts it as a
+press.
+.
+Currently a press is equivalent to putting the touchpad in trackstick
+emulation mode. Property: "Synaptics Finger"
+.TP 7
+.BI "Option \*qMaxTapTime\*q \*q" integer \*q
+Maximum time (in milliseconds) for detecting a tap. Property: "Synaptics Tap
+Durations"
+.TP 7
+.BI "Option \*qMaxTapMove\*q \*q" integer \*q
+Maximum movement of the finger for detecting a tap. Property: "Synaptics Tap
+Move"
+.TP 7
+.BI "Option \*qMaxDoubleTapTime\*q \*q" integer \*q
+Maximum time (in milliseconds) for detecting a double tap. Property:
+"Synaptics Tap Durations"
+.TP 7
+.BI "Option \*qClickTime\*q \*q" integer \*q
+The duration of the mouse click generated by tapping. Property: "Synaptics Tap
+Durations"
+.TP 7
+.BI "Option \*qClickPad\*q \*q" boolean \*q
+Whether the device is a click pad. See
+.B ClickPad support
+for more details. Property: "Synaptics ClickPad"
+.TP 7
+.BI "Option \*qFastTaps\*q \*q" boolean \*q
+Makes the driver react faster to a single tap, but also makes double
+clicks caused by double tapping slower. Property: "Synaptics Tap FastTap"
+.TP 7
+.BI "Option \*qVertEdgeScroll\*q \*q" boolean \*q
+Enable vertical scrolling when dragging along the right edge. Property:
+"Synaptics Edge Scrolling"
+.TP 7
+.BI "Option \*qHorizEdgeScroll\*q \*q" boolean \*q
+Enable horizontal scrolling when dragging along the bottom edge. Property:
+"Synaptics Edge Scrolling"
+.TP 7
+.BI "Option \*qCornerCoasting\*q \*q" boolean \*q
+Enable edge scrolling to continue while the finger stays in an edge corner.
+Property: "Synaptics Edge Scrolling"
+.TP 7
+.BI "Option \*qVertTwoFingerScroll\*q \*q" boolean \*q
+Enable vertical scrolling when dragging with two fingers anywhere on
+the touchpad. Property: "Synaptics Two-Finger Scrolling"
+.TP 7
+.BI "Option \*qHorizTwoFingerScroll\*q \*q" boolean \*q
+Enable horizontal scrolling when dragging with two fingers anywhere on
+the touchpad. Property: "Synaptics Two-Finger Scrolling"
+.TP 7
+.BI "Option \*qVertScrollDelta\*q \*q" integer \*q
+Move distance of the finger for a scroll event. Property: "Synaptics Scrolling
+Distance"
+.TP 7
+.BI "Option \*qHorizScrollDelta\*q \*q" integer \*q
+Move distance of the finger for a scroll event. Property: "Synaptics Scrolling
+Distance"
+.TP 7
+.BI "Option \*qEdgeMotionMinZ\*q \*q" integer \*q
+Finger pressure at which minimum edge motion speed is set. Property:
+"Synaptics Edge Motion Pressure"
+.TP
+.BI "Option \*qEdgeMotionMaxZ\*q \*q" integer \*q
+Finger pressure at which maximum edge motion speed is set. Property:
+"Synaptics Edge Motion Pressure"
+.TP
+.BI "Option \*qEdgeMotionMinSpeed\*q \*q" integer \*q
+Slowest setting for edge motion speed. Property: "Synaptics Edge Motion Speed"
+.TP
+.BI "Option \*qEdgeMotionMaxSpeed\*q \*q" integer \*q
+Fastest setting for edge motion speed. Property: "Synaptics Edge Motion Speed"
+.TP
+.BI "Option \*qEdgeMotionUseAlways\*q \*q" boolean \*q
+If on, edge motion is also used for normal movements.
+.
+If off, edge motion is used only when dragging. Property: "Synaptics Edge
+Motion Always"
+.TP
+.BI "Option \*qMinSpeed\*q \*q" float \*q
+Minimum speed factor. Property: "Synaptics Move Speed"
+.TP
+.BI "Option \*qMaxSpeed\*q \*q" float \*q
+Maximum speed factor. Property: "Synaptics Move Speed"
+.TP
+.BI "Option \*qAccelFactor\*q \*q" float \*q
+Acceleration factor for normal pointer movements. Property: "Synaptics Move
+Speed"
+.TP
+.BI "Option \*qTrackstickSpeed\*q \*q" float \*q
+Speed scale when in trackstick emulation mode. Property: "Synaptics Move Speed"
+.TP
+.BI "Option \*qPressureMotionMinZ\*q \*q" integer \*q
+Finger pressure at which minimum pressure motion factor is applied. Property:
+"Synaptics Pressure Motion"
+.TP
+.BI "Option \*qPressureMotionMaxZ\*q \*q" integer \*q
+Finger pressure at which maximum pressure motion factor is applied. Property:
+"Synaptics Pressure Motion"
+.TP
+.BI "Option \*qPressureMotionMinFactor\*q \*q" integer \*q
+Lowest setting for pressure motion factor. Property: "Synaptics Pressure
+Motion Factor"
+.TP
+.BI "Option \*qPressureMotionMaxFactor\*q \*q" integer \*q
+Greatest setting for pressure motion factor. Property: "Synaptics Pressure
+Motion Factor"
+.TP
+.BI "Option \*qHorizHysteresis\*q \*q" integer \*q
+The minimum horizontal HW distance required to generate motion events. Can be
+specified as a percentage. Increase if noise motion is a problem for you. Zero
+is disabled.
+Default: 0.5 percent of the diagonal or (in case of evdev) the appropriate
+"fuzz" as advertised by the device.
+.TP
+.BI "Option \*qVertHysteresis\*q \*q" integer \*q
+The minimum vertical HW distance required to generate motion events. See
+\fBHorizHysteresis\fR.
+.TP
+.BI "Option \*qUpDownScrolling\*q \*q" boolean \*q
+If on, the up/down buttons generate button 4/5 events.
+.
+If off, the up button generates a double click and the down button
+generates a button 2 event. This option is only available for touchpads with
+physical scroll buttons.
+Property: "Synaptics Button Scrolling"
+.TP
+.BI "Option \*qLeftRightScrolling\*q \*q" boolean \*q
+If on, the left/right buttons generate button 6/7 events.
+.
+If off, the left/right buttons both generate button 2 events.
+This option is only available for touchpads with physical scroll buttons.
+Property: "Synaptics Button Scrolling"
+.TP
+.BI "Option \*qUpDownScrollRepeat\*q \*q" boolean \*q
+If on, and the up/down buttons are used for scrolling
+(\fBUpDownScrolling\fR), these buttons will send auto-repeating 4/5 events,
+with the delay between repeats determined by \fBScrollButtonRepeat\fR.
+This option is only available for touchpads with physical scroll buttons.
+Property: "Synaptics Button Scrolling Repeat"
+.TP
+.BI "Option \*qLeftRightScrollRepeat\*q \*q" boolean \*q
+If on, and the left/right buttons are used for scrolling
+(\fBLeftRightScrolling\fR), these buttons will send auto-repeating 6/7 events,
+with the delay between repeats determined by \fBScrollButtonRepeat\fR.
+This option is only available for touchpads with physical scroll buttons.
+Property: "Synaptics Button Scrolling Repeat"
+.TP
+.BI "Option \*qScrollButtonRepeat\*q \*q" integer \*q
+The number of milliseconds between repeats of button events 4-7 from the
+up/down/left/right scroll buttons.
+This option is only available for touchpads with physical scroll buttons.
+Property: "Synaptics Button Scrolling Time"
+.TP
+.BI "Option \*qEmulateMidButtonTime\*q \*q" integer \*q
+Maximum time (in milliseconds) for middle button emulation. Property:
+"Synaptics Middle Button Timeout"
+.TP
+.BI "Option \*qEmulateTwoFingerMinZ\*q \*q" integer \*q
+For touchpads not capable of detecting multiple fingers but are capable
+of detecting finger pressure and width, this sets the
+Z pressure threshold. When both Z pressure and W width thresholds
+are crossed, a two finger press will be emulated. This defaults
+to a value that disables emulation on touchpads with real two-finger detection
+and defaults to a value that enables emulation on remaining touchpads that
+support pressure and width support.
+Property: "Synaptics Two-Finger Pressure"
+.TP
+.BI "Option \*qEmulateTwoFingerMinW\*q \*q" integer \*q
+For touchpads not capable of detecting multiple fingers but are
+capable of detecting finger width and pressure, this sets the
+W width threshold. When both W width and Z pressure thresholds
+are crossed, a two finger press will be emulated. This feature works best
+with (\fBPalmDetect\fR) off. Property: "Synaptics Two-Finger Width"
+.TP
+.BI "Option \*qTouchpadOff\*q \*q" integer \*q
+Switch off the touchpad.
+.
+Valid values are:
+.TS
+l l.
+0 Touchpad is enabled
+1 Touchpad is switched off
+2 Only tapping and scrolling is switched off
+.TE
+Property: "Synaptics Off"
+.TP
+.BI "Option \*qLockedDrags\*q \*q" boolean \*q
+If off, a tap-and-drag gesture ends when you release the finger.
+.
+If on, the gesture is active until you tap a second time, or until
+LockedDragTimeout expires. Property: "Synaptics Locked Drags"
+.TP
+.BI "Option \*qLockedDragTimeout\*q \*q" integer \*q
+This parameter specifies how long it takes (in milliseconds) for the
+LockedDrags mode to be automatically turned off after the finger is
+released from the touchpad. Property: "Synaptics Locked Drags Timeout"
+.TP
+.BI "Option \*qRTCornerButton\*q \*q" integer \*q
+.
+Which mouse button is reported on a right top corner tap.
+.
+Set to 0 to disable. Property: "Synaptics Tap Action"
+.TP
+.BI "Option \*qRBCornerButton\*q \*q" integer \*q
+Which mouse button is reported on a right bottom corner tap.
+.
+Set to 0 to disable. Property: "Synaptics Tap Action"
+.TP
+.BI "Option \*qLTCornerButton\*q \*q" integer \*q
+Which mouse button is reported on a left top corner tap.
+.
+Set to 0 to disable. Property: "Synaptics Tap Action"
+.TP
+.BI "Option \*qLBCornerButton\*q \*q" integer \*q
+Which mouse button is reported on a left bottom corner tap.
+.
+Set to 0 to disable. Property: "Synaptics Tap Action"
+.TP
+.BI "Option \*qTapButton1\*q \*q" integer \*q
+Which mouse button is reported on a non-corner one-finger tap.
+.
+Set to 0 to disable. Property: "Synaptics Tap Action"
+.TP
+.BI "Option \*qTapButton2\*q \*q" integer \*q
+Which mouse button is reported on a non-corner two-finger tap.
+.
+Set to 0 to disable. Property: "Synaptics Tap Action"
+.TP
+.BI "Option \*qTapButton3\*q \*q" integer \*q
+Which mouse button is reported on a non-corner three-finger tap.
+.
+Set to 0 to disable. Property: "Synaptics Tap Action"
+.TP
+.BI "Option \*qClickFinger1\*q \*q" integer \*q
+Which mouse button is reported when left-clicking with one finger.
+.
+Set to 0 to disable. Property: "Synaptics Click Action"
+.TP
+.BI "Option \*qClickFinger2\*q \*q" integer \*q
+Which mouse button is reported when left-clicking with two fingers.
+.
+Set to 0 to disable. Property: "Synaptics Click Action"
+.TP
+.BI "Option \*qClickFinger3\*q \*q" integer \*q
+Which mouse button is reported when left-clicking with three fingers.
+.
+Set to 0 to disable. Property: "Synaptics Click Action"
+.TP
+.BI "Option \*qCircularScrolling\*q \*q" boolean \*q
+If on, circular scrolling is used. Property: "Synaptics Circular Scrolling"
+.TP
+.BI "Option \*qCircScrollDelta\*q \*q" float \*q
+Move angle (radians) of finger to generate a scroll event. Property: "Synaptics
+Circular Scrolling Distance"
+.TP
+.BI "Option \*qCircScrollTrigger\*q \*q" integer \*q
+Trigger region on the touchpad to start circular scrolling
+.TS
+l l.
+0 All Edges
+1 Top Edge
+2 Top Right Corner
+3 Right Edge
+4 Bottom Right Corner
+5 Bottom Edge
+6 Bottom Left Corner
+7 Left Edge
+8 Top Left Corner
+.TE
+Property: "Synaptics Circular Scrolling Trigger"
+.TP
+.BI "Option \*qCircularPad\*q \*q" boolean \*q
+.
+Instead of being a rectangle, the edge is the ellipse enclosed by the
+Left/Right/Top/BottomEdge parameters.
+.
+For circular touchpads. Property: "Synaptics Circular Pad"
+.TP
+.BI "Option \*qPalmDetect\*q \*q" boolean \*q
+If palm detection should be enabled.
+.
+Note that this also requires hardware/firmware support from the
+touchpad. Property: "Synaptics Palm Detection"
+.TP
+.BI "Option \*qPalmMinWidth\*q \*q" integer \*q
+Minimum finger width at which touch is considered a palm. Property: "Synaptics
+Palm Dimensions"
+.TP
+.BI "Option \*qPalmMinZ\*q \*q" integer \*q
+Minimum finger pressure at which touch is considered a palm. Property:
+"Synaptics Palm Dimensions"
+.TP
+.BI "Option \*qCoastingSpeed\*q \*q" float \*q
+Your finger needs to produce this many scrolls per second in order to start
+coasting. The default is 20 which should prevent you from starting coasting
+unintentionally.
+.
+0 disables coasting. Property: "Synaptics Coasting Speed"
+.TP
+.BI "Option \*qCoastingFriction\*q \*q" float \*q
+Number of scrolls/second² to decrease the coasting speed. Default
+is 50.
+Property: "Synaptics Coasting Speed"
+.TP
+.BI "Option \*qSingleTapTimeout\*q \*q" integer \*q
+Timeout after a tap to recognize it as a single tap. Property: "Synaptics Tap
+Durations"
+.TP
+.BI "Option \*qGrabEventDevice\*q \*q" boolean \*q
+If GrabEventDevice is true, the driver will grab the event device for
+exclusive use when using the linux 2.6 event protocol.
+.
+When using other protocols, this option has no effect.
+.
+Grabbing the event device means that no other user space or kernel
+space program sees the touchpad events.
+.
+This is desirable if the X config file includes /dev/input/mice as an
+input device, but is undesirable if you want to monitor the device
+from user space.
+.
+When changing this parameter with the synclient program, the change
+will not take effect until the synaptics driver is disabled and
+reenabled.
+.
+This can be achieved by switching to a text console and then switching
+back to X.
+.
+.
+.TP
+.BI "Option \*qTapAndDragGesture\*q \*q" boolean \*q
+Switch on/off the tap-and-drag gesture.
+.
+This gesture is an alternative way of dragging.
+.
+It is performed by tapping (touching and releasing the finger), then
+touching again and moving the finger on the touchpad.
+.
+The gesture is enabled by default and can be disabled by setting the
+TapAndDragGesture option to false. Property: "Synaptics Gestures"
+.
+.TP
+.BI "Option \*qVertResolution\*q \*q" integer \*q
+Resolution of X coordinates in units/millimeter. The value is used
+together with HorizResolution to compensate unequal vertical and
+horizontal sensitivity. Setting VertResolution and HorizResolution
+equal values means no compensation. Default value is read from
+the touchpad or set to 1 if value could not be read.
+Property: "Synaptics Pad Resolution"
+.
+.TP
+.BI "Option \*qHorizResolution\*q \*q" integer \*q
+Resolution of Y coordinates in units/millimeter. The value is used
+together with VertResolution to compensate unequal vertical and
+horizontal sensitivity. Setting VertResolution and HorizResolution
+equal values means no compensation. Default value is read from
+the touchpad or set to 1 if value could not be read.
+Property: "Synaptics Pad Resolution"
+.
+.TP
+.BI "Option \*qAreaLeftEdge\*q \*q" integer \*q
+Ignore movements, scrolling and tapping which take place left of this edge.
+.
+The option is disabled by default and can be enabled by setting the
+AreaLeftEdge option to any integer value other than zero. If supported by the
+server (version 1.9 and later), the edge may be specified in percent of
+the total width of the touchpad. Property: "Synaptics Area"
+.
+.TP
+.BI "Option \*qAreaRightEdge\*q \*q" integer \*q
+Ignore movements, scrolling and tapping which take place right of this edge.
+.
+The option is disabled by default and can be enabled by setting the
+AreaRightEdge option to any integer value other than zero. If supported by the
+server (version 1.9 and later), the edge may be specified in percent of
+the total width of the touchpad. Property: "Synaptics Area"
+.
+.TP
+.BI "Option \*qAreaTopEdge\*q \*q" integer \*q
+Ignore movements, scrolling and tapping which take place above this edge.
+.
+The option is disabled by default and can be enabled by setting the
+AreaTopEdge option to any integer value other than zero. If supported by the
+server (version 1.9 and later), the edge may be specified in percent of
+the total height of the touchpad. Property: "Synaptics Area"
+.
+.TP
+.BI "Option \*qAreaBottomEdge\*q \*q" integer \*q
+Ignore movements, scrolling and tapping which take place below this edge.
+.
+The option is disabled by default and can be enabled by setting the
+AreaBottomEdge option to any integer value other than zero. If supported by the
+server (version 1.9 and later), the edge may be specified in percent of
+the total height of the touchpad. Property: "Synaptics Area"
+.
+.TP
+.BI "Option \*qSoftButtonAreas\*q \*q" "RBL RBR RBT RBB MBL MBR MBT MBB" \*q
+This option is only available on ClickPad devices.
+Enable soft button click area support on ClickPad devices.
+The first four parameters are the left, right, top, bottom edge of the right
+button, respectively, the second four parameters are the left, right, top,
+bottom edge of the middle button, respectively. Any of the values may be
+given as percentage of the touchpad width or height, whichever applies.
+If any edge is set to 0, the button is assumed to extend to infinity in the
+given direction. Setting all values to 0 disables soft button areas.
+Property: "Synaptics Soft Button Areas"
+.
+
+.SH CONFIGURATION DETAILS
+.SS Area handling
+The LeftEdge, RightEdge, TopEdge and BottomEdge parameters are used to
+define the edge and corner areas of the touchpad.
+.
+The parameters split the touchpad area in 9 pieces, like this:
+.LP
+.TS
+l|l|lsls
+---
+|c|cw(5P)|c|l
+---
+|c|c|c|l
+|c|c|c|l
+|c|c|c|l
+---
+|c|c|c|l
+---
+|lsl|ll.
+ LeftEdge RightEdge
+ Physical top edge
+1 2 3
+ TopEdge
+
+4 5 6
+
+ BottomEdge
+7 8 9
+ Physical bottom edge
+Physical left edge Physical right edge
+.TE
+.LP
+Coordinates to the left of LeftEdge are part of the left edge (areas
+1, 4 and 7), coordinates to the left of LeftEdge and above TopEdge
+(area 1) are part of the upper left corner, etc.
+.PP
+A good way to find appropriate edge parameters is to enable the
+SHMConfig option and run "synclient \-m 1" to see the x/y coordinates
+corresponding to different positions on the touchpad.
+.PP
+The perceived physical edges may be adjusted with the AreaLeftEdge,
+AreaRightEdge, AreaTopEdge, and AreaBottomEdge options. If these values are
+set to something other than the physical edges, input in the space between
+the area edge and the respective physical edge is ignored. Note that this
+reduces the available space on the touchpad.
+.SS Tapping
+A tap event happens when the finger is touched and released in a time
+interval shorter than MaxTapTime, and the touch and release
+coordinates are less than MaxTapMove units apart.
+.
+A "touch" event happens when the Z value goes above FingerHigh, and an
+"untouch" event happens when the Z value goes below FingerLow.
+.
+.LP
+The MaxDoubleTapTime parameter has the same function as the MaxTapTime
+parameter, but for the second, third, etc tap in a tap sequence.
+.
+If you can't perform double clicks fast enough (for example, xmms
+depends on fast double clicks), try reducing this parameter.
+.
+If you can't get word selection to work in xterm (ie button down,
+button up, button down, move mouse), try increasing this parameter.
+.
+.LP
+The ClickTime parameter controls the delay between the button down and
+button up X events generated in response to a tap event.
+.
+A too long value can cause undesirable autorepeat in scroll bars and a
+too small value means that visual feedback from the gui application
+you are interacting with is harder to see.
+.
+For this parameter to have any effect, "FastTaps" has to be disabled.
+.SS Acceleration
+The MinSpeed, MaxSpeed and AccelFactor parameters control the pointer
+motion speed.
+.
+The speed value defines the scaling between touchpad coordinates and
+screen coordinates.
+.
+When moving the finger very slowly, the MinSpeed value is used, when
+moving very fast the MaxSpeed value is used.
+.
+When moving the finger at moderate speed, you get a pointer motion
+speed somewhere between MinSpeed and MaxSpeed.
+.
+If you don't want any acceleration, set MinSpeed and MaxSpeed to the
+same value.
+.
+.LP
+The MinSpeed, MaxSpeed and AccelFactor parameters don't have any
+effect on scrolling speed.
+.
+Scrolling speed is determined solely from the VertScrollDelta and
+HorizScrollDelta parameters.
+.
+To disable vertical or horizontal scrolling, set VertScrollDelta or
+HorizScrollDelta to zero.
+.
+To invert the direction of vertical or horizontal scrolling, set
+VertScrollDelta or HorizScrollDelta to a negative value.
+.
+.LP
+Acceleration is mostly handled outside the driver, thus the driver will
+translate MinSpeed into constant deceleration and adapt MaxSpeed at
+startup time. This ensures you can user the other acceleration profiles, albeit
+without pressure motion. However the numbers at runtime will likely be different
+from any options you may have set.
+
+.SS Pressure motion
+When pressure motion is activated, the cursor motion speed depends
+on the pressure exerted on the touchpad (the more pressure exerted on
+the touchpad, the faster the pointer).
+.
+More precisely the speed is first calculated according to MinSpeed,
+MaxSpeed and AccelFactor, and then is multiplied by a sensitivity
+factor.
+.
+.LP
+The sensitivity factor can be adjusted using the PressureMotion
+parameters.
+.
+If the pressure is below PressureMotionMinZ, PressureMotionMinFactor
+is used, and if the pressure is greater than PressureMotionMaxZ,
+PressureMotionMaxFactor is used.
+.
+By default, PressureMotionMinZ and PressureMotionMaxZ are equal to
+EdgeMotionMinZ and EdgeMotionMaxZ.
+.
+For a pressure value between PressureMotionMinZ and
+PressureMotionMaxZ, the factor is increased linearly.
+.
+.SS Edge motion
+When hitting an edge, movement can be automatically continued.
+.
+If EdgeMotionUseAlways is false, edge motion is only used when
+dragging.
+.
+With EdgeMotionUseAlways set to true, it is also used for normal
+cursor movements.
+.
+.LP
+Edge motion speed is calculated by taking into account the amount of
+pressure applied to the touchpad.
+.
+The sensitivity can be adjusted using the EdgeMotion parameters.
+.
+If the pressure is below EdgeMotionMinZ, EdgeMotionMinSpeed is used,
+and if the pressure is greater than EdgeMotionMaxZ, EdgeMotionMaxSpeed
+is used.
+.
+For a pressure value between EdgeMotionMinZ and EdgeMotionMaxZ, the
+speed is increased linearly.
+.
+.SS Middle button emulation
+Since most synaptics touchpad models don't have a button that
+corresponds to the middle button on a mouse, the driver can emulate
+middle mouse button events.
+.
+If you press both the left and right mouse buttons at almost the same
+time (no more than EmulateMidButtonTime milliseconds apart) the driver
+generates a middle mouse button event.
+.
+.SS Circular scrolling
+Circular scrolling acts like a scrolling wheel on the touchpad.
+.
+Scrolling is engaged when a drag starts in the given CircScrollTrigger
+region, which can be all edges, a particular side, or a particular
+corner.
+.
+Once scrolling is engaged, moving your finger in clockwise circles
+around the center of the touchpad will generate scroll down events and
+counter clockwise motion will generate scroll up events.
+.
+Lifting your finger will disengage circular scrolling.
+.
+Use tight circles near the center of the pad for fast scrolling and
+large circles for better control.
+.
+When used together with vertical scrolling, hitting the upper or lower
+right corner will seamlessly switch over from vertical to circular
+scrolling.
+
+.SS Coasting
+Coasting is enabled by setting the CoastingSpeed parameter to a
+non-zero value.
+.
+Coasting comes in two flavors: conventional (finger off) coasting, and
+corner (finger on) coasting.
+.LP
+Conventional coasting is enabled when coasting is enabled,
+and CornerCoasting is set to false.
+.
+When conventional coasting is enabled, horizontal/vertical scrolling
+can continue after the finger is released from the lower/right edge of
+the touchpad.
+.
+The driver computes the scrolling speed corresponding to the finger
+speed immediately before the finger leaves the touchpad.
+.
+If this scrolling speed is larger than the CoastingSpeed parameter
+(measured in scroll events per second), the scrolling will continue
+with the same speed in the same direction until the finger touches the
+touchpad again.
+.
+.LP
+Corner coasting is enabled when coasting is enabled, and
+CornerCoasting is set to true.
+.
+When corner coasting is enabled, edge scrolling can continue as long
+as the finger stays in a corner.
+.
+Coasting begins when the finger enters the corner, and continues until
+the finger leaves the corner.
+.
+CornerCoasting takes precedence over the seamless switch from edge
+scrolling to circular scrolling. That is, if CornerCoasting is
+active, scrolling will stop, and circular scrolling will not start,
+when the finger leaves the corner.
+
+.SS Noise cancellation
+The synaptics has a built-in noise cancellation based on hysteresis. This means
+that incoming coordinates actually shift a box of predefined dimensions such
+that it covers the incoming coordinate, and only the boxes own center is used
+as input. Obviously, the smaller the box the better, but the likelyhood of
+noise motion coming through also increases.
+
+.SS Trackstick mode
+Trackstick emulation mode is entered when pressing the finger hard on
+the touchpad.
+.
+The FingerPress parameter controls the minimum required finger
+pressure.
+.
+If the finger hasn't moved more than MaxTapMove after MaxTapTime has
+elapsed, trackstick mode is entered.
+.
+In this mode, moving the finger slightly in any direction gives a
+speed vector that moves the pointer.
+.
+The TrackstickSpeed parameter controls the ratio between pointer speed
+and finger movement distance.
+.
+Trackstick mode is exited when the finger pressure drops below
+FingerLow or when the finger is moved further than MaxTapMove away
+from the initial position.
+
+.SS ClickPad support
+A click pad device has button(s) integrated into the touchpad surface. The
+user must press downward on the touchpad in order to generated a button
+press. ClickPad support is enabled if the option
+.B ClickPad
+is set or the property is set at runtime. On some platforms, this option
+will be set automatically if the kernel detects a matching device. On Linux,
+the device must have the INPUT_PROP_BUTTONPAD property set.
+.LP
+ClickPads do not support middle mouse button emulation. If enabling ClickPad
+support at runime, the user must also set the middle mouse button timeout to
+0. If auto-detected, middle mouse button emulation is disabled by the
+driver.
+.LP
+ClickPads provide software emulated buttons through
+.B Option SoftButtonAreas.
+These buttons enable areas on the touchpad to perform as right or middle
+mouse button. When the user performs a click within a defined soft button
+area, a right or middle click is performed.
+
+.SH "DEVICE PROPERTIES"
+Synaptics 1.0 and higher support input device properties if the driver is
+running on X server 1.6 or higher. On these driver versions, Option
+"SHMConfig" is not needed to enable run-time configuration. The synclient tool
+shipped with synaptics version 1.1 uses input device properties by default.
+.
+Properties supported:
+.TP 7
+.BI "Synaptics Edges"
+32 bit, 4 values, left, right, top, bottom.
+
+.TP 7
+.BI "Synaptics Finger"
+32 bit, 3 values, low, high, press.
+
+.TP 7
+.BI "Synaptics Tap Time"
+32 bit.
+
+.TP 7
+.BI "Synaptics Tap Move"
+32 bit.
+
+.TP 7
+.BI "Synaptics Tap Durations"
+32 bit, 3 values, single touch timeout, max tapping time for double taps,
+duration of a single click.
+
+.TP 7
+.BI "Synaptics ClickPad"
+8 bit (Bool).
+
+.TP 7
+.BI "Synaptics Tap FastTap"
+8 bit (BOOL).
+
+.TP 7
+.BI "Synaptics Middle Button Timeout"
+32 bit.
+
+.TP 7
+.BI "Synaptics Two-Finger Pressure"
+32 bit.
+
+.TP 7
+.BI "Synaptics Two-Finger Width"
+32 bit.
+
+.TP 7
+.BI "Synaptics Scrolling Distance"
+32 bit, 2 values, vert, horiz.
+
+.TP 7
+.BI "Synaptics Edge Scrolling"
+8 bit (BOOL), 3 values, vertical, horizontal, corner.
+
+.TP 7
+.BI "Synaptics Two-Finger Scrolling"
+8 bit (BOOL), 2 values, vertical, horizontal.
+
+.TP 7
+.BI "Synaptics Move Speed"
+FLOAT, 4 values, min, max, accel, trackstick.
+
+.TP 7
+.BI "Synaptics Edge Motion Pressure"
+32 bit, 2 values, min, max.
+
+.TP 7
+.BI "Synaptics Edge Motion Speed"
+32 bit, 2 values, min, max.
+
+.TP 7
+.BI "Synaptics Edge Motion Always"
+8 bit (BOOL).
+
+.TP 7
+.BI "Synaptics Button Scrolling"
+8 bit (BOOL), 2 values, updown, leftright.
+
+.TP 7
+.BI "Synaptics Button Scrolling Repeat"
+8 bit (BOOL), 2 values, updown, leftright.
+
+.TP 7
+.BI "Synaptics Button Scrolling Time"
+32 bit.
+
+.TP 7
+.BI "Synaptics Off"
+8 bit, valid values (0, 1, 2).
+
+.TP 7
+.BI "Synaptics Locked Drags"
+8 bit (BOOL).
+
+.TP 7
+.BI "Synaptics Locked Drags Timeout"
+32 bit.
+
+.TP 7
+.BI "Synaptics Tap Action"
+8 bit, up to MAX_TAP values (see synaptics.h), 0 disables an element. order:
+RT, RB, LT, LB, F1, F2, F3.
+
+.TP 7
+.BI "Synaptics Click Action"
+8 bit, up to MAX_CLICK values (see synaptics.h), 0 disables an element.
+order: Finger 1, 2, 3.
+
+.TP 7
+.BI "Synaptics Circular Scrolling"
+8 bit (BOOL).
+
+.TP 7
+.BI "Synaptics Circular Scrolling Distance"
+FLOAT.
+
+.TP 7
+.BI "Synaptics Circular Scrolling Trigger"
+8 bit, valid values 0..8 (inclusive) order: any edge, top, top + right,
+right, right + bottom, bottom, bottom + left, left, left + top.
+
+.TP 7
+.BI "Synaptics Circular Pad"
+8 bit (BOOL).
+
+.TP 7
+.BI "Synaptics Palm Detection"
+8 bit (BOOL).
+
+.TP 7
+.BI "Synaptics Palm Dimensions"
+32 bit, 2 values, width, z.
+
+.TP 7
+.BI "Synaptics Coasting Speed"
+FLOAT, 2 values, speed, friction.
+
+.TP 7
+.BI "Synaptics Pressure Motion"
+32 bit, 2 values, min, max.
+
+.TP 7
+.BI "Synaptics Pressure Motion Factor"
+FLOAT, 2 values, min, max.
+
+.TP 7
+.BI "Synaptics Grab Event Device"
+8 bit (BOOL).
+
+.TP 7
+.BI "Synaptics Gestures"
+8 bit (BOOL), 1 value, tap-and-drag.
+
+.TP 7
+.BI "Synaptics Area"
+The AreaLeftEdge, AreaRightEdge, AreaTopEdge and AreaBottomEdge parameters are used to
+define the edges of the active area of the touchpad. All movements, scrolling and tapping
+which take place outside of this area will be ignored. This property is disabled by
+default.
+
+32 bit, 4 values, left, right, top, bottom. 0 disables an element.
+
+.TP 7
+.BI "Synaptics Soft Button Areas"
+This property is only available on ClickPad devices.
+The Right and middle soft button areas are used to support right and middle
+click actions on a ClickPad device. Providing 0 for all values of a given button
+disables the button area.
+
+32 bit, 8 values, RBL, RBR, RBT, RBB, MBL, MBR, MBT, MBB.
+
+.TP 7
+.BI "Synaptics Capabilities"
+This read-only property expresses the physical capability of the touchpad,
+most notably whether the touchpad hardware supports multi-finger tapping and
+scrolling.
+
+8 bit (BOOL), 7 values (read-only), has left button, has middle button, has
+right button, two-finger detection, three-finger detection, pressure detection, and finger/palm width detection.
+
+.TP 7
+.BI "Synaptics Pad Resolution"
+32 bit unsigned, 2 values (read-only), vertical, horizontal in units/millimeter.
+
+.SH "NOTES"
+Configuration through
+.I InputClass
+sections is recommended in X servers 1.8 and later. See xorg.conf.d(5) for
+more details. An example xorg.conf.d snippet is provided in
+.I ${sourcecode}/conf/50-synaptics.conf
+.LP
+Configuration through hal fdi files is recommended in X servers 1.5, 1.6 and
+1.7. An example hal policy file is provided in
+.I ${sourcecode}/conf/11-x11-synaptics.fdi
+.LP
+If either of
+.BI "Protocol \*q" auto-dev \*q
+(default) or
+.BI "Protocol \*q" event \*q
+is used, the driver initializes defaults based on the capabilities reported by
+the kernel driver. Acceleration, edges and resolution are based on the dimensions
+reported by the kernel. If the kernel reports multi-finger detection, two-finger
+vertical scrolling is enabled, horizontal two-finger scrolling is disabled and
+edge scrolling is disabled. If no multi-finger capabilities are reported,
+edge scrolling is enabled for both horizontal and vertical scrolling.
+Tapping is disabled by default for touchpads with one or more physical buttons.
+To enable it you need to map tap actions to buttons. See the "TapButton1",
+"TapButton2" and "TapButton3" options.
+.LP
+Button mapping for physical buttons is handled in the server.
+If the device is switched to left-handed (an in-server mapping of physical
+buttons 1, 2, 3 to the logical buttons 3, 2, 1, respectively), both physical
+and TapButtons are affected. To counteract this, the TapButtons need to be set
+up in reverse order (TapButton1=3, TapButton2=1).
+
+.SH "REMOVED OPTIONS"
+The following options are no longer part of the driver configuration:
+.TP
+.BI "Option \*qRepeater\*q \*q" string \*q
+.TP
+.BI "Option \*qHistorySize\*q \*q" integer \*q
+.TP
+.BI "Option \*qSpecialScrollAreaRight\*q \*q" boolean \*q
+.TP
+.BI "Option \*qGuestMouseOff\*q \*q" boolean \*q
+
+.SH "AUTHORS"
+.LP
+Peter Osterlund <petero2@telia.com> and many others.
+.SH "SEE ALSO"
+.LP
+__xservername__(__appmansuffix__), __xconfigfile__(__filemansuffix__), Xserver(__appmansuffix__), X(__miscmansuffix__), synclient(__appmansuffix__), syndaemon(__appmansuffix__)
diff --git a/man/synclient.man b/man/synclient.man
new file mode 100644
index 0000000..5938a32
--- /dev/null
+++ b/man/synclient.man
@@ -0,0 +1,116 @@
+.\" shorthand for double quote that works everywhere.
+.ds q \N'34'
+.TH synclient __appmansuffix__ __vendorversion__
+.SH NAME
+.LP
+synclient \- commandline utility to query and modify Synaptics driver
+options.
+.SH "SYNOPSIS"
+.LP
+synclient [\fI\-m interval\fP]
+.br
+synclient [\fI\-hlV?\fP] [var1=value1 [var2=value2] ...]
+.SH "DESCRIPTION"
+.LP
+This program lets you change your Synaptics TouchPad driver for
+XOrg/XFree86 server parameters while X is running.
+
+For the -m and -h options, SHM must be enabled by setting the option SHMConfig
+"on" in your XOrg/XFree86 configuration.
+.SH "OPTIONS"
+.LP
+.TP
+\fB\-m interval\fR
+monitor changes to the touchpad state.
+.
+Interval specifies how often (in ms) to poll the touchpad state.
+.
+Whenever a change in the touchpad state is detected, one line of
+output is generated that describes the current state of the touchpad.
+This option is only available in SHM mode.
+.
+The following data is included in the output.
+.RS
+.TP
+\fBtime\fR
+Time in seconds since the logging was started.
+.TP
+\fBx,y\fR
+The x/y coordinates of the finger on the touchpad.
+.
+The origin is in the upper left corner.
+.TP
+\fBz\fR
+The "pressure" value.
+.
+Pressing the finger harder on the touchpad typically produces a larger
+value.
+.
+Note that most touchpads don't measure the real pressure though.
+.
+Instead, capacitance is usually measured, which is correlated to the
+contact area between the finger and the touchpad.
+.
+Since more pressure usually means a larger contact area, the reported
+pressure value is at least indirectly related to the real pressure.
+.TP
+\fBf\fR
+The number of fingers currently touching the touchpad.
+.
+Note that only some touchpads can report more than one finger.
+.
+Generally, synaptics touchpads can, but ALPS touchpads can't.
+.TP
+\fBw\fR
+The w value is a measurement of the finger width.
+.
+This is only supported by some synaptics touchpads.
+.
+Touchpads that can't measure the finger width typically report a faked
+constant value when a finger is touching the touchpad.
+.TP
+\fBl,r,u,d,m,multi\fR
+The state of the left, right, up, down, middle and multi buttons.
+.
+Zero means not pressed, one means pressed.
+.
+Not all touchpads have all these buttons.
+.
+If a button doesn't exist, the value is always reported as 0.
+.RE
+.TP
+\fB\-l\fR
+List current user settings. This is the default if no option is given.
+.TP
+\fB\-V\fR
+Print version number and exit.
+.TP
+\fB\-?\fR
+Show the help message.
+.TP
+\fBvar=value\fR
+Set user parameter \fIvar\fR to \fIvalue\fR.
+
+
+.SH "FILES"
+.LP
+\fI/etc/X11/xorg.conf\fP
+.LP
+\fI/etc/X11/XF86Config\-4\fP
+.SH "EXAMPLES"
+.LP
+To disable EdgeMotionSpeed:
+.LP
+synclient EdgeMotionSpeed=0
+.LP
+To monitor touchpad events (requires SHM):
+.LP
+synclient \-m 100
+.SH "AUTHORS"
+.LP
+Peter Osterlund <petero2@telia.com> and many others.
+.TP
+This man page was written by Mattia Dongili <malattia@debian.org>
+.SH "SEE ALSO"
+.LP
+__xservername__(__appmansuffix__), syndaemon(__appmansuffix__), synaptics(__drivermansuffix__)
diff --git a/man/syndaemon.man b/man/syndaemon.man
new file mode 100644
index 0000000..a9d69b0
--- /dev/null
+++ b/man/syndaemon.man
@@ -0,0 +1,101 @@
+.\" shorthand for double quote that works everywhere.
+.ds q \N'34'
+.TH syndaemon __appmansuffix__ __vendorversion__
+.SH NAME
+.LP
+syndaemon \- a program that monitors keyboard activity and disables
+the touchpad when the keyboard is being used.
+.SH "SYNOPSIS"
+.LP
+syndaemon [\fI\-i idle\-time\fP] [\fI\-m poll-inverval\fP] [\fI\-d\fP] [\fI\-p pid\-file\fP]
+[\fI\-t\fP] [\fI\-k\fP] [\fI\-K\fP] [\fI\-R\fP]
+.SH "DESCRIPTION"
+.LP
+Disabling the touchpad while typing avoids unwanted movements of the
+pointer that could lead to giving focus to the wrong window.
+.
+.SH "OPTIONS"
+.LP
+.TP
+\fB\-i\fR <\fIidle\-time\fP>
+How many seconds to wait after the last key press before enabling the
+touchpad.
+.
+(default is 2.0s).
+.LP
+.TP
+\fB\-m\fR <\fIpoll\-interval\fP>
+How many milliseconds to wait between two polling intervals. If this value is
+too low, it will cause unnecessary wake-ups. If this value is too high,
+some key presses (press and release happen between two intervals) may not
+be noticed. This switch has no effect when running with
+\fB-R\fP.
+.
+Default is 200ms.
+.LP
+.TP
+\fB\-d\fP
+Start as a daemon, ie in the background.
+.LP
+.TP
+\fB\-p\fR <\fIpid\-file\fP>
+Create a pid file with the specified filename.
+.
+A pid file will only be created if the program is started in daemon
+mode.
+.LP
+.TP
+\fB\-t\fP
+Only disable tapping and scrolling, not mouse movements, in response
+to keyboard activity.
+.LP
+.TP
+\fB\-k\fP
+Ignore modifier keys when monitoring keyboard activity.
+.LP
+.TP
+\fB\-K\fP
+Like \-k but also ignore Modifier+Key combos.
+.LP
+.TP
+\fB\-R\fP
+Use the XRecord extension for detecting keyboard activity instead of polling
+the keyboard state.
+.SH "ENVIRONMENT VARIABLES"
+.LP
+.TP
+\fBDISPLAY\fP
+Specifies the X server to contact.
+.SH EXIT CODES
+If syndaemon exists with a return code other than 0, the error encountered
+is as below.
+.LP
+.TP
+\fBExit code 1
+Invalid commandline argument.
+.LP
+.TP
+\fBExit code 2
+The connection to the X sever could not be established or no touchpad device
+could be found.
+.LP
+.TP
+\fBExit code 3
+The fork into daemon mode failed or the pid file could not be created.
+.LP
+.TP
+\fBExit code 4
+XRECORD requested but not available or usable on the server.
+.SH "CAVEATS"
+.LP
+It doesn't make much sense to connect to a remote X server, because
+the daemon will then monitor the remote server for keyboard activity,
+but will disable the touchpad on the local machine.
+.SH "AUTHORS"
+.LP
+Peter Osterlund <petero2@telia.com>.
+.TP
+This man page was written by Mattia Dongili <malattia@debian.org>
+.SH "SEE ALSO"
+.LP
+__xservername__(__appmansuffix__), synclient(__appmansuffix__), synaptics(__drivermansuffix__)
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..5333ba5
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,55 @@
+# 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.
+@DRIVER_NAME@_drv_la_LTLIBRARIES = @DRIVER_NAME@_drv.la
+@DRIVER_NAME@_drv_la_LDFLAGS = -module -avoid-version
+@DRIVER_NAME@_drv_ladir = @inputdir@
+
+AM_CPPFLAGS = -I$(top_srcdir)/include
+AM_CFLAGS = $(XORG_CFLAGS)
+
+@DRIVER_NAME@_drv_la_SOURCES = @DRIVER_NAME@.c synapticsstr.h \
+ synproto.c \
+ synproto.h \
+ properties.c
+
+if BUILD_PS2COMM
+@DRIVER_NAME@_drv_la_SOURCES += \
+ alpscomm.c \
+ ps2comm.c ps2comm.h
+endif
+
+if BUILD_EVENTCOMM
+@DRIVER_NAME@_drv_la_SOURCES += \
+ eventcomm.c eventcomm.h
+@DRIVER_NAME@_drv_la_LIBADD = \
+ $(MTDEV_LIBS)
+endif
+
+if BUILD_PSMCOMM
+@DRIVER_NAME@_drv_la_SOURCES += \
+ psmcomm.c
+endif
diff --git a/src/alpscomm.c b/src/alpscomm.c
new file mode 100644
index 0000000..0bf0447
--- /dev/null
+++ b/src/alpscomm.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright © 2001 Stefan Gmeiner
+ * Copyright © 2003 Neil Brown
+ * Copyright © 2003-2005,2007 Peter Osterlund
+ *
+ * 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:
+ * Stefan Gmeiner (riddlebox@freesurf.ch)
+ * Neil Brown (neilb@cse.unsw.edu.au)
+ * Peter Osterlund (petero2@telia.com)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <xorg-server.h>
+#include "synproto.h"
+#include "synaptics.h"
+#include "synapticsstr.h"
+#include "ps2comm.h"
+#include <xf86.h>
+
+/* Wait for the channel to go silent, which means we're in sync */
+static void
+ALPS_sync(int fd)
+{
+ byte buffer[64];
+
+ while (xf86WaitForInput(fd, 250000) > 0) {
+ xf86ReadSerial(fd, &buffer, 64);
+ }
+}
+
+/*
+ * send the ALPS init sequence, ie 4 consecutive "disable"s before the "enable"
+ * This "magic knock" is performed both for the trackpad and for the pointing
+ * stick. Not all models have a pointing stick, but trying to initialize it
+ * anyway doesn't seem to hurt.
+ */
+static void
+ALPS_initialize(int fd)
+{
+ xf86FlushInput(fd);
+ ps2_putbyte(fd, PS2_CMD_SET_DEFAULT);
+ ps2_putbyte(fd, PS2_CMD_SET_SCALING_2_1);
+ ps2_putbyte(fd, PS2_CMD_SET_SCALING_2_1);
+ ps2_putbyte(fd, PS2_CMD_SET_SCALING_2_1);
+ ps2_putbyte(fd, PS2_CMD_DISABLE);
+
+ ps2_putbyte(fd, PS2_CMD_DISABLE);
+ ps2_putbyte(fd, PS2_CMD_DISABLE);
+ ps2_putbyte(fd, PS2_CMD_DISABLE);
+ ps2_putbyte(fd, PS2_CMD_DISABLE);
+ ps2_putbyte(fd, PS2_CMD_ENABLE);
+
+ ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1);
+ ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1);
+ ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1);
+ ps2_putbyte(fd, PS2_CMD_DISABLE);
+
+ ps2_putbyte(fd, PS2_CMD_DISABLE);
+ ps2_putbyte(fd, PS2_CMD_DISABLE);
+ ps2_putbyte(fd, PS2_CMD_DISABLE);
+ ps2_putbyte(fd, PS2_CMD_DISABLE);
+ ps2_putbyte(fd, PS2_CMD_ENABLE);
+
+ ALPS_sync(fd);
+}
+
+static Bool
+ALPSQueryHardware(InputInfoPtr pInfo)
+{
+ ALPS_initialize(pInfo->fd);
+ return TRUE;
+}
+
+static Bool
+ALPS_packet_ok(struct CommData *comm)
+{
+ /* ALPS absolute mode packets start with 0b11111mrl */
+ if ((comm->protoBuf[0] & 0xf8) == 0xf8)
+ return TRUE;
+ return FALSE;
+}
+
+static Bool
+ALPS_get_packet(struct CommData *comm, InputInfoPtr pInfo)
+{
+ int c;
+
+ while ((c = XisbRead(comm->buffer)) >= 0) {
+ unsigned char u = (unsigned char) c;
+
+ comm->protoBuf[comm->protoBufTail++] = u;
+
+ if (comm->protoBufTail == 3) { /* PS/2 packet received? */
+ if ((comm->protoBuf[0] & 0xc8) == 0x08) {
+ comm->protoBufTail = 0;
+ return TRUE;
+ }
+ }
+
+ if (comm->protoBufTail >= 6) { /* Full packet received */
+ comm->protoBufTail = 0;
+ if (ALPS_packet_ok(comm))
+ return TRUE;
+ while ((c = XisbRead(comm->buffer)) >= 0); /* If packet is invalid, re-sync */
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * ALPS abolute Mode
+ * byte 0: 1 1 1 1 1 mid0 rig0 lef0
+ * byte 1: 0 x6 x5 x4 x3 x2 x1 x0
+ * byte 2: 0 x10 x9 x8 x7 up1 fin ges
+ * byte 3: 0 y9 y8 y7 1 mid1 rig1 lef1
+ * byte 4: 0 y6 y5 y4 y3 y2 y1 y0
+ * byte 5: 0 z6 z5 z4 z3 z2 z1 z0
+ *
+ * On a dualpoint, {mid,rig,lef}0 are the stick, 1 are the pad.
+ * We just 'or' them together for now.
+ *
+ * The touchpad on an 'Acer Aspire' has 4 buttons:
+ * left,right,up,down.
+ * This device always sets {mid,rig,lef}0 to 1 and
+ * reflects left,right,down,up in lef1,rig1,mid1,up1.
+ */
+static void
+ALPS_process_packet(unsigned char *packet, struct SynapticsHwState *hw)
+{
+ int x = 0, y = 0, z = 0;
+ int left = 0, right = 0, middle = 0;
+ int i;
+
+ hw->millis = GetTimeInMillis();
+
+ x = (packet[1] & 0x7f) | ((packet[2] & 0x78) << (7 - 3));
+ y = (packet[4] & 0x7f) | ((packet[3] & 0x70) << (7 - 4));
+ z = packet[5];
+
+ if (z == 127) { /* DualPoint stick is relative, not absolute */
+ hw->left = packet[3] & 1;
+ hw->right = (packet[3] >> 1) & 1;
+ return;
+ }
+
+ /* Handle normal packets */
+ hw->x = hw->y = hw->z = hw->numFingers = hw->fingerWidth = 0;
+ hw->left = hw->right = hw->up = hw->down = hw->middle = FALSE;
+ for (i = 0; i < 8; i++)
+ hw->multi[i] = FALSE;
+
+ if (z > 0) {
+ hw->x = x;
+ hw->y = y;
+ }
+ hw->z = z;
+ hw->numFingers = (z > 0) ? 1 : 0;
+ hw->fingerWidth = 5;
+
+ left |= (packet[2]) & 1;
+ left |= (packet[3]) & 1;
+ right |= (packet[3] >> 1) & 1;
+ if (packet[0] == 0xff) {
+ int back = (packet[3] >> 2) & 1;
+ int forward = (packet[2] >> 2) & 1;
+
+ if (back && forward) {
+ middle = 1;
+ back = 0;
+ forward = 0;
+ }
+ hw->down = back;
+ hw->up = forward;
+ }
+ else {
+ left |= (packet[0]) & 1;
+ right |= (packet[0] >> 1) & 1;
+ middle |= (packet[0] >> 2) & 1;
+ middle |= (packet[3] >> 2) & 1;
+ }
+
+ hw->left = left;
+ hw->right = right;
+ hw->middle = middle;
+}
+
+static Bool
+ALPSReadHwState(InputInfoPtr pInfo,
+ struct CommData *comm, struct SynapticsHwState *hwRet)
+{
+ unsigned char *buf = comm->protoBuf;
+ struct SynapticsHwState *hw = comm->hwState;
+
+ if (!ALPS_get_packet(comm, pInfo))
+ return FALSE;
+
+ ALPS_process_packet(buf, hw);
+
+ SynapticsCopyHwState(hwRet, hw);
+ return TRUE;
+}
+
+struct SynapticsProtocolOperations alps_proto_operations = {
+ NULL,
+ NULL,
+ ALPSQueryHardware,
+ ALPSReadHwState,
+ NULL,
+ NULL
+};
diff --git a/src/eventcomm.c b/src/eventcomm.c
new file mode 100644
index 0000000..280ef9b
--- /dev/null
+++ b/src/eventcomm.c
@@ -0,0 +1,997 @@
+/*
+ * Copyright © 2004-2007 Peter Osterlund
+ *
+ * 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 Osterlund (petero2@telia.com)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <xorg-server.h>
+#include "eventcomm.h"
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+#include "synproto.h"
+#include "synaptics.h"
+#include "synapticsstr.h"
+#include <xf86.h>
+#ifdef HAVE_MULTITOUCH
+#include <mtdev.h>
+#endif
+
+#ifndef INPUT_PROP_BUTTONPAD
+#define INPUT_PROP_BUTTONPAD 0x02
+#endif
+#ifndef INPUT_PROP_SEMI_MT
+#define INPUT_PROP_SEMI_MT 0x03
+#endif
+
+#define SYSCALL(call) while (((call) == -1) && (errno == EINTR))
+
+#define LONG_BITS (sizeof(long) * 8)
+#define NBITS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
+#define OFF(x) ((x) % LONG_BITS)
+#define LONG(x) ((x) / LONG_BITS)
+#define TEST_BIT(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
+
+/**
+ * Protocol-specific data.
+ */
+struct eventcomm_proto_data {
+ /**
+ * Do we need to grab the event device?
+ * Note that in the current flow, this variable is always false and
+ * exists for readability of the code.
+ */
+ BOOL need_grab;
+ int st_to_mt_offset[2];
+ double st_to_mt_scale[2];
+#ifdef HAVE_MULTITOUCH
+ struct mtdev *mtdev;
+ int axis_map[MT_ABS_SIZE];
+ int cur_slot;
+ ValuatorMask **last_mt_vals;
+ int num_touches;
+#endif
+};
+
+struct eventcomm_proto_data *
+EventProtoDataAlloc(void)
+{
+ struct eventcomm_proto_data *proto_data;
+
+ proto_data = calloc(1, sizeof(struct eventcomm_proto_data));
+ if (!proto_data)
+ return NULL;
+
+ proto_data->st_to_mt_scale[0] = 1;
+ proto_data->st_to_mt_scale[1] = 1;
+
+ return proto_data;
+}
+
+#ifdef HAVE_MULTITOUCH
+static int
+last_mt_vals_slot(const SynapticsPrivate * priv)
+{
+ struct eventcomm_proto_data *proto_data =
+ (struct eventcomm_proto_data *) priv->proto_data;
+ int value = proto_data->cur_slot - proto_data->mtdev->caps.slot.minimum;
+
+ return value < priv->num_slots ? value : -1;
+}
+
+static void
+UninitializeTouch(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ struct eventcomm_proto_data *proto_data =
+ (struct eventcomm_proto_data *) priv->proto_data;
+
+ if (!priv->has_touch)
+ return;
+
+ if (proto_data->last_mt_vals) {
+ int i;
+
+ for (i = 0; i < priv->num_slots; i++)
+ valuator_mask_free(&proto_data->last_mt_vals[i]);
+ free(proto_data->last_mt_vals);
+ proto_data->last_mt_vals = NULL;
+ }
+
+ mtdev_close(proto_data->mtdev);
+ proto_data->mtdev = NULL;
+ proto_data->num_touches = 0;
+}
+
+static void
+InitializeTouch(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ struct eventcomm_proto_data *proto_data =
+ (struct eventcomm_proto_data *) priv->proto_data;
+ int i;
+
+ if (!priv->has_touch)
+ return;
+
+ proto_data->mtdev = mtdev_new_open(pInfo->fd);
+ if (!proto_data->mtdev) {
+ xf86IDrvMsg(pInfo, X_WARNING,
+ "failed to create mtdev instance, ignoring touch events\n");
+ return;
+ }
+
+ proto_data->cur_slot = proto_data->mtdev->caps.slot.value;
+ proto_data->num_touches = 0;
+
+ proto_data->last_mt_vals = calloc(priv->num_slots, sizeof(ValuatorMask *));
+ if (!proto_data->last_mt_vals) {
+ xf86IDrvMsg(pInfo, X_WARNING,
+ "failed to allocate MT last values mask array\n");
+ UninitializeTouch(pInfo);
+ return;
+ }
+
+ for (i = 0; i < priv->num_slots; i++) {
+ int j;
+
+ proto_data->last_mt_vals[i] = valuator_mask_new(4 + priv->num_mt_axes);
+ if (!proto_data->last_mt_vals[i]) {
+ xf86IDrvMsg(pInfo, X_WARNING,
+ "failed to allocate MT last values mask\n");
+ UninitializeTouch(pInfo);
+ return;
+ }
+
+ /* Axes 0-4 are for X, Y, and scrolling. num_mt_axes does not include X
+ * and Y. */
+ valuator_mask_set(proto_data->last_mt_vals[i], 0, 0);
+ valuator_mask_set(proto_data->last_mt_vals[i], 1, 0);
+ for (j = 0; j < priv->num_mt_axes; j++)
+ valuator_mask_set(proto_data->last_mt_vals[i], 4 + j, 0);
+ }
+}
+#endif
+
+static Bool
+EventDeviceOnHook(InputInfoPtr pInfo, SynapticsParameters * para)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ struct eventcomm_proto_data *proto_data =
+ (struct eventcomm_proto_data *) priv->proto_data;
+
+ if (para->grab_event_device) {
+ /* Try to grab the event device so that data don't leak to /dev/input/mice */
+ int ret;
+
+ SYSCALL(ret = ioctl(pInfo->fd, EVIOCGRAB, (pointer) 1));
+ if (ret < 0) {
+ xf86IDrvMsg(pInfo, X_WARNING, "can't grab event device, errno=%d\n",
+ errno);
+ return FALSE;
+ }
+ }
+
+ proto_data->need_grab = FALSE;
+
+#ifdef HAVE_MULTITOUCH
+ InitializeTouch(pInfo);
+#endif
+
+ return TRUE;
+}
+
+static Bool
+EventDeviceOffHook(InputInfoPtr pInfo)
+{
+#ifdef HAVE_MULTITOUCH
+ UninitializeTouch(pInfo);
+#endif
+
+ return Success;
+}
+
+/**
+ * Test if the device on the file descriptior is recognized as touchpad
+ * device. Required bits for touchpad recognition are:
+ * - ABS_X + ABS_Y for absolute axes
+ * - ABS_PRESSURE or BTN_TOUCH
+ * - BTN_TOOL_FINGER
+ * - BTN_TOOL_PEN is _not_ set
+ *
+ * @param fd The file descriptor to an event device.
+ * @param test_grab If true, test whether an EVIOCGRAB is possible on the
+ * device. A failure to grab the event device returns in a failure.
+ *
+ * @return TRUE if the device is a touchpad or FALSE otherwise.
+ */
+static Bool
+event_query_is_touchpad(int fd, BOOL test_grab)
+{
+ int ret = FALSE, rc;
+ unsigned long evbits[NBITS(EV_MAX)] = { 0 };
+ unsigned long absbits[NBITS(ABS_MAX)] = { 0 };
+ unsigned long keybits[NBITS(KEY_MAX)] = { 0 };
+
+ if (test_grab) {
+ SYSCALL(rc = ioctl(fd, EVIOCGRAB, (pointer) 1));
+ if (rc < 0)
+ return FALSE;
+ }
+
+ /* Check for ABS_X, ABS_Y, ABS_PRESSURE and BTN_TOOL_FINGER */
+
+ SYSCALL(rc = ioctl(fd, EVIOCGBIT(0, sizeof(evbits)), evbits));
+ if (rc < 0)
+ goto unwind;
+ if (!TEST_BIT(EV_SYN, evbits) ||
+ !TEST_BIT(EV_ABS, evbits) || !TEST_BIT(EV_KEY, evbits))
+ goto unwind;
+
+ SYSCALL(rc = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits));
+ if (rc < 0)
+ goto unwind;
+ if (!TEST_BIT(ABS_X, absbits) || !TEST_BIT(ABS_Y, absbits))
+ goto unwind;
+
+ SYSCALL(rc = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits));
+ if (rc < 0)
+ goto unwind;
+
+ /* we expect touchpad either report raw pressure or touches */
+ if (!TEST_BIT(ABS_PRESSURE, absbits) && !TEST_BIT(BTN_TOUCH, keybits))
+ goto unwind;
+ /* all Synaptics-like touchpad report BTN_TOOL_FINGER */
+ if (!TEST_BIT(BTN_TOOL_FINGER, keybits))
+ goto unwind;
+ if (TEST_BIT(BTN_TOOL_PEN, keybits))
+ goto unwind; /* Don't match wacom tablets */
+
+ ret = TRUE;
+
+ unwind:
+ if (test_grab)
+ SYSCALL(ioctl(fd, EVIOCGRAB, (pointer) 0));
+
+ return (ret == TRUE);
+}
+
+typedef struct {
+ short vendor;
+ short product;
+ enum TouchpadModel model;
+} model_lookup_t;
+
+#define PRODUCT_ANY 0x0000
+
+static model_lookup_t model_lookup_table[] = {
+ {0x0002, 0x0007, MODEL_SYNAPTICS},
+ {0x0002, 0x0008, MODEL_ALPS},
+ {0x05ac, PRODUCT_ANY, MODEL_APPLETOUCH},
+ {0x0002, 0x000e, MODEL_ELANTECH},
+ {0x0, 0x0, 0x0}
+};
+
+/**
+ * Check for the vendor/product id on the file descriptor and compare
+ * with the built-in model LUT. This information is used in synaptics.c to
+ * initialize model-specific dimensions.
+ *
+ * @param fd The file descriptor to a event device.
+ * @param[out] model_out The type of touchpad model detected.
+ *
+ * @return TRUE on success or FALSE otherwise.
+ */
+static Bool
+event_query_model(int fd, enum TouchpadModel *model_out,
+ unsigned short *vendor_id, unsigned short *product_id)
+{
+ struct input_id id;
+ int rc;
+ model_lookup_t *model_lookup;
+
+ SYSCALL(rc = ioctl(fd, EVIOCGID, &id));
+ if (rc < 0)
+ return FALSE;
+
+ for (model_lookup = model_lookup_table; model_lookup->vendor;
+ model_lookup++) {
+ if (model_lookup->vendor == id.vendor &&
+ (model_lookup->product == id.product ||
+ model_lookup->product == PRODUCT_ANY))
+ *model_out = model_lookup->model;
+ }
+
+ *vendor_id = id.vendor;
+ *product_id = id.product;
+
+ return TRUE;
+}
+
+/**
+ * Get absinfo information from the given file descriptor for the given
+ * ABS_FOO code and store the information in min, max, fuzz and res.
+ *
+ * @param fd File descriptor to an event device
+ * @param code Event code (e.g. ABS_X)
+ * @param[out] min Minimum axis range
+ * @param[out] max Maximum axis range
+ * @param[out] fuzz Fuzz of this axis. If NULL, fuzz is ignored.
+ * @param[out] res Axis resolution. If NULL or the current kernel does not
+ * support the resolution field, res is ignored
+ *
+ * @return Zero on success, or errno otherwise.
+ */
+static int
+event_get_abs(InputInfoPtr pInfo, int fd, int code,
+ int *min, int *max, int *fuzz, int *res)
+{
+ int rc;
+ struct input_absinfo abs = { 0 };
+
+ SYSCALL(rc = ioctl(fd, EVIOCGABS(code), &abs));
+ if (rc < 0) {
+ xf86IDrvMsg(pInfo, X_ERROR, "%s EVIOCGABS error on %d (%s)\n",
+ __func__, code, strerror(errno));
+ return errno;
+ }
+
+ *min = abs.minimum;
+ *max = abs.maximum;
+ /* We dont trust a zero fuzz as it probably is just a lazy value */
+ if (fuzz && abs.fuzz > 0)
+ *fuzz = abs.fuzz;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)
+ if (res)
+ *res = abs.resolution;
+#endif
+
+ return 0;
+}
+
+/* Query device for axis ranges */
+static void
+event_query_axis_ranges(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ struct eventcomm_proto_data *proto_data = priv->proto_data;
+ unsigned long absbits[NBITS(ABS_MAX)] = { 0 };
+ unsigned long keybits[NBITS(KEY_MAX)] = { 0 };
+ char buf[256] = { 0 };
+ int rc;
+
+ /* The kernel's fuzziness concept seems a bit weird, but it can more or
+ * less be applied as hysteresis directly, i.e. no factor here. */
+ event_get_abs(pInfo, pInfo->fd, ABS_X, &priv->minx, &priv->maxx,
+ &priv->synpara.hyst_x, &priv->resx);
+
+ event_get_abs(pInfo, pInfo->fd, ABS_Y, &priv->miny, &priv->maxy,
+ &priv->synpara.hyst_y, &priv->resy);
+
+ priv->has_pressure = FALSE;
+ priv->has_width = FALSE;
+ SYSCALL(rc = ioctl(pInfo->fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits));
+ if (rc >= 0) {
+ priv->has_pressure = (TEST_BIT(ABS_PRESSURE, absbits) != 0);
+ priv->has_width = (TEST_BIT(ABS_TOOL_WIDTH, absbits) != 0);
+ }
+ else
+ xf86IDrvMsg(pInfo, X_ERROR, "failed to query ABS bits (%s)\n",
+ strerror(errno));
+
+ if (priv->has_pressure)
+ event_get_abs(pInfo, pInfo->fd, ABS_PRESSURE, &priv->minp, &priv->maxp,
+ NULL, NULL);
+
+ if (priv->has_width)
+ event_get_abs(pInfo, pInfo->fd, ABS_TOOL_WIDTH,
+ &priv->minw, &priv->maxw, NULL, NULL);
+
+#if HAVE_MULTITOUCH
+ if (priv->has_touch) {
+ int st_minx = priv->minx;
+ int st_maxx = priv->maxx;
+ int st_miny = priv->miny;
+ int st_maxy = priv->maxy;
+
+ event_get_abs(pInfo, pInfo->fd, ABS_MT_POSITION_X, &priv->minx,
+ &priv->maxx, &priv->synpara.hyst_x, &priv->resx);
+ event_get_abs(pInfo, pInfo->fd, ABS_MT_POSITION_Y, &priv->miny,
+ &priv->maxy, &priv->synpara.hyst_y, &priv->resy);
+
+ proto_data->st_to_mt_offset[0] = priv->minx - st_minx;
+ proto_data->st_to_mt_scale[0] =
+ (priv->maxx - priv->minx) / (st_maxx - st_minx);
+ proto_data->st_to_mt_offset[1] = priv->miny - st_miny;
+ proto_data->st_to_mt_scale[1] =
+ (priv->maxy - priv->miny) / (st_maxy - st_miny);
+ }
+#endif
+
+ SYSCALL(rc = ioctl(pInfo->fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits));
+ if (rc >= 0) {
+ priv->has_left = (TEST_BIT(BTN_LEFT, keybits) != 0);
+ priv->has_right = (TEST_BIT(BTN_RIGHT, keybits) != 0);
+ priv->has_middle = (TEST_BIT(BTN_MIDDLE, keybits) != 0);
+ priv->has_double = (TEST_BIT(BTN_TOOL_DOUBLETAP, keybits) != 0);
+ priv->has_triple = (TEST_BIT(BTN_TOOL_TRIPLETAP, keybits) != 0);
+
+ if ((TEST_BIT(BTN_0, keybits) != 0) ||
+ (TEST_BIT(BTN_1, keybits) != 0) ||
+ (TEST_BIT(BTN_2, keybits) != 0) || (TEST_BIT(BTN_3, keybits) != 0))
+ priv->has_scrollbuttons = 1;
+ }
+
+ /* Now print the device information */
+ xf86IDrvMsg(pInfo, X_PROBED, "x-axis range %d - %d\n",
+ priv->minx, priv->maxx);
+ xf86IDrvMsg(pInfo, X_PROBED, "y-axis range %d - %d\n",
+ priv->miny, priv->maxy);
+ if (priv->has_pressure)
+ xf86IDrvMsg(pInfo, X_PROBED, "pressure range %d - %d\n",
+ priv->minp, priv->maxp);
+ else
+ xf86IDrvMsg(pInfo, X_INFO,
+ "device does not report pressure, will use touch data.\n");
+ if (priv->has_width)
+ xf86IDrvMsg(pInfo, X_PROBED, "finger width range %d - %d\n",
+ priv->minw, priv->maxw);
+ else
+ xf86IDrvMsg(pInfo, X_INFO, "device does not report finger width.\n");
+
+ if (priv->has_left)
+ strcat(buf, " left");
+ if (priv->has_right)
+ strcat(buf, " right");
+ if (priv->has_middle)
+ strcat(buf, " middle");
+ if (priv->has_double)
+ strcat(buf, " double");
+ if (priv->has_triple)
+ strcat(buf, " triple");
+ if (priv->has_scrollbuttons)
+ strcat(buf, " scroll-buttons");
+
+ xf86IDrvMsg(pInfo, X_PROBED, "buttons:%s\n", buf);
+}
+
+static Bool
+EventQueryHardware(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ struct eventcomm_proto_data *proto_data = priv->proto_data;
+
+ if (!event_query_is_touchpad
+ (pInfo->fd, (proto_data) ? proto_data->need_grab : TRUE))
+ return FALSE;
+
+ xf86IDrvMsg(pInfo, X_PROBED, "touchpad found\n");
+
+ return TRUE;
+}
+
+static Bool
+SynapticsReadEvent(InputInfoPtr pInfo, struct input_event *ev)
+{
+#ifdef HAVE_MULTITOUCH
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ struct eventcomm_proto_data *proto_data = priv->proto_data;
+#endif
+ int rc = TRUE;
+ ssize_t len;
+
+#ifdef HAVE_MULTITOUCH
+ if (proto_data->mtdev)
+ len = mtdev_get(proto_data->mtdev, pInfo->fd, ev, 1) *
+ sizeof(struct input_event);
+ else
+#endif
+ len = read(pInfo->fd, ev, sizeof(*ev));
+ if (len <= 0) {
+ /* We use X_NONE here because it doesn't alloc */
+ if (errno != EAGAIN)
+ xf86MsgVerb(X_NONE, 0, "%s: Read error %s\n", pInfo->name,
+ strerror(errno));
+ rc = FALSE;
+ }
+ else if (len % sizeof(*ev)) {
+ xf86MsgVerb(X_NONE, 0, "%s: Read error, invalid number of bytes.",
+ pInfo->name);
+ rc = FALSE;
+ }
+ return rc;
+}
+
+#ifdef HAVE_MULTITOUCH
+static Bool
+EventTouchSlotPreviouslyOpen(SynapticsPrivate * priv, int slot)
+{
+ int i;
+
+ for (i = 0; i < priv->num_active_touches; i++)
+ if (priv->open_slots[i] == slot)
+ return TRUE;
+
+ return FALSE;
+}
+#endif
+
+static void
+EventProcessTouchEvent(InputInfoPtr pInfo, struct SynapticsHwState *hw,
+ struct input_event *ev)
+{
+#ifdef HAVE_MULTITOUCH
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ struct eventcomm_proto_data *proto_data = priv->proto_data;
+
+ if (!priv->has_touch)
+ return;
+
+ if (ev->code == ABS_MT_SLOT) {
+ proto_data->cur_slot = ev->value;
+ }
+ else {
+ int slot_index = last_mt_vals_slot(priv);
+
+ if (slot_index < 0)
+ return;
+
+ if (hw->slot_state[slot_index] == SLOTSTATE_OPEN_EMPTY)
+ hw->slot_state[slot_index] = SLOTSTATE_UPDATE;
+ if (ev->code == ABS_MT_TRACKING_ID) {
+ if (ev->value >= 0) {
+ hw->slot_state[slot_index] = SLOTSTATE_OPEN;
+ proto_data->num_touches++;
+
+ if (slot_index >= 0)
+ valuator_mask_copy(hw->mt_mask[slot_index],
+ proto_data->last_mt_vals[slot_index]);
+ else
+ xf86IDrvMsg(pInfo, X_WARNING,
+ "Attempted to copy values from out-of-range "
+ "slot, touch events may be incorrect.\n");
+ }
+ else if (hw->slot_state[slot_index] != SLOTSTATE_EMPTY) {
+ hw->slot_state[slot_index] = SLOTSTATE_CLOSE;
+ proto_data->num_touches--;
+ }
+ }
+ else {
+ int map = proto_data->axis_map[ev->code - ABS_MT_TOUCH_MAJOR];
+
+ valuator_mask_set(hw->mt_mask[slot_index], map, ev->value);
+ if (slot_index >= 0) {
+ ValuatorMask *mask = proto_data->last_mt_vals[slot_index];
+ int last_val = valuator_mask_get(mask, map);
+
+ if (EventTouchSlotPreviouslyOpen(priv, slot_index)) {
+ if (ev->code == ABS_MT_POSITION_X)
+ hw->cumulative_dx += ev->value - last_val;
+ else if (ev->code == ABS_MT_POSITION_Y)
+ hw->cumulative_dy += ev->value - last_val;
+ }
+
+ valuator_mask_set(mask, map, ev->value);
+ }
+ }
+ }
+#endif
+}
+
+/**
+ * Count the number of fingers based on the CommData information.
+ * The CommData struct contains the event information based on previous
+ * struct input_events, now we're just counting based on that.
+ *
+ * @param comm Assembled information from previous events.
+ * @return The number of fingers currently set.
+ */
+static int
+count_fingers(InputInfoPtr pInfo, const struct CommData *comm)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ struct eventcomm_proto_data *proto_data = priv->proto_data;
+ int fingers = 0;
+
+ if (comm->oneFinger)
+ fingers = 1;
+ else if (comm->twoFingers)
+ fingers = 2;
+ else if (comm->threeFingers)
+ fingers = 3;
+
+#ifdef HAVE_MULTITOUCH
+ if (priv->has_touch && proto_data->num_touches > fingers)
+ fingers = proto_data->num_touches;
+#endif
+
+ return fingers;
+}
+
+static inline double
+apply_st_scaling(struct eventcomm_proto_data *proto_data, int value, int axis)
+{
+ return value * proto_data->st_to_mt_scale[axis] +
+ proto_data->st_to_mt_offset[axis];
+}
+
+Bool
+EventReadHwState(InputInfoPtr pInfo,
+ struct CommData *comm, struct SynapticsHwState *hwRet)
+{
+ struct input_event ev;
+ Bool v;
+ struct SynapticsHwState *hw = comm->hwState;
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ SynapticsParameters *para = &priv->synpara;
+ struct eventcomm_proto_data *proto_data = priv->proto_data;
+
+ SynapticsResetTouchHwState(hw, FALSE);
+
+ /* Reset cumulative values if buttons were not previously pressed */
+ if (!hw->left && !hw->right && !hw->middle) {
+ hw->cumulative_dx = hw->x;
+ hw->cumulative_dy = hw->y;
+ }
+
+ while (SynapticsReadEvent(pInfo, &ev)) {
+ switch (ev.type) {
+ case EV_SYN:
+ switch (ev.code) {
+ case SYN_REPORT:
+ hw->numFingers = count_fingers(pInfo, comm);
+ hw->millis = 1000 * ev.time.tv_sec + ev.time.tv_usec / 1000;
+ SynapticsCopyHwState(hwRet, hw);
+ return TRUE;
+ }
+ break;
+ case EV_KEY:
+ v = (ev.value ? TRUE : FALSE);
+ switch (ev.code) {
+ case BTN_LEFT:
+ hw->left = v;
+ break;
+ case BTN_RIGHT:
+ hw->right = v;
+ break;
+ case BTN_MIDDLE:
+ hw->middle = v;
+ break;
+ case BTN_FORWARD:
+ hw->up = v;
+ break;
+ case BTN_BACK:
+ hw->down = v;
+ break;
+ case BTN_0:
+ hw->multi[0] = v;
+ break;
+ case BTN_1:
+ hw->multi[1] = v;
+ break;
+ case BTN_2:
+ hw->multi[2] = v;
+ break;
+ case BTN_3:
+ hw->multi[3] = v;
+ break;
+ case BTN_4:
+ hw->multi[4] = v;
+ break;
+ case BTN_5:
+ hw->multi[5] = v;
+ break;
+ case BTN_6:
+ hw->multi[6] = v;
+ break;
+ case BTN_7:
+ hw->multi[7] = v;
+ break;
+ case BTN_TOOL_FINGER:
+ comm->oneFinger = v;
+ break;
+ case BTN_TOOL_DOUBLETAP:
+ comm->twoFingers = v;
+ break;
+ case BTN_TOOL_TRIPLETAP:
+ comm->threeFingers = v;
+ break;
+ case BTN_TOUCH:
+ if (!priv->has_pressure)
+ hw->z = v ? para->finger_high + 1 : 0;
+ break;
+ }
+ break;
+ case EV_ABS:
+ if (ev.code < ABS_MT_SLOT) {
+ switch (ev.code) {
+ case ABS_X:
+ hw->x = apply_st_scaling(proto_data, ev.value, 0);
+ break;
+ case ABS_Y:
+ hw->y = apply_st_scaling(proto_data, ev.value, 1);
+ break;
+ case ABS_PRESSURE:
+ hw->z = ev.value;
+ break;
+ case ABS_TOOL_WIDTH:
+ hw->fingerWidth = ev.value;
+ break;
+ }
+ }
+ else
+ EventProcessTouchEvent(pInfo, hw, &ev);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+/* filter for the AutoDevProbe scandir on /dev/input */
+static int
+EventDevOnly(const struct dirent *dir)
+{
+ return strncmp(EVENT_DEV_NAME, dir->d_name, 5) == 0;
+}
+
+#ifdef HAVE_MULTITOUCH
+static void
+event_query_touch(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ SynapticsParameters *para = &priv->synpara;
+ struct eventcomm_proto_data *proto_data = priv->proto_data;
+ struct mtdev *mtdev;
+ int i;
+ int rc;
+ uint8_t prop;
+
+ priv->max_touches = 0;
+ priv->num_mt_axes = 0;
+
+#ifdef EVIOCGPROP
+ SYSCALL(rc = ioctl(pInfo->fd, EVIOCGPROP(sizeof(prop)), &prop));
+ if (rc >= 0 && BitIsOn(&prop, INPUT_PROP_SEMI_MT)) {
+ xf86IDrvMsg(pInfo, X_INFO,
+ "ignoring touch events for semi-multitouch device\n");
+ priv->has_semi_mt = TRUE;
+ }
+
+ if (rc >= 0 && BitIsOn(&prop, INPUT_PROP_BUTTONPAD)) {
+ xf86IDrvMsg(pInfo, X_INFO, "found clickpad property\n");
+ para->clickpad = TRUE;
+ }
+#endif
+
+ mtdev = mtdev_new_open(pInfo->fd);
+ if (!mtdev) {
+ xf86IDrvMsg(pInfo, X_WARNING,
+ "failed to open mtdev when querying touch capabilities\n");
+ return;
+ }
+
+ for (i = 0; i < MT_ABS_SIZE; i++) {
+ if (mtdev->caps.has_abs[i]) {
+ switch (i) {
+ /* X and Y axis info is handed by synaptics already */
+ case ABS_MT_POSITION_X - ABS_MT_TOUCH_MAJOR:
+ case ABS_MT_POSITION_Y - ABS_MT_TOUCH_MAJOR:
+ /* Skip tracking ID info */
+ case ABS_MT_TRACKING_ID - ABS_MT_TOUCH_MAJOR:
+ break;
+ default:
+ priv->num_mt_axes++;
+ break;
+ }
+ priv->has_touch = TRUE;
+ }
+ }
+
+ if (priv->has_touch) {
+ int axnum;
+
+ static const char *labels[] = {
+ AXIS_LABEL_PROP_ABS_MT_TOUCH_MAJOR,
+ AXIS_LABEL_PROP_ABS_MT_TOUCH_MINOR,
+ AXIS_LABEL_PROP_ABS_MT_WIDTH_MAJOR,
+ AXIS_LABEL_PROP_ABS_MT_WIDTH_MINOR,
+ AXIS_LABEL_PROP_ABS_MT_ORIENTATION,
+ AXIS_LABEL_PROP_ABS_MT_POSITION_X,
+ AXIS_LABEL_PROP_ABS_MT_POSITION_Y,
+ AXIS_LABEL_PROP_ABS_MT_TOOL_TYPE,
+ AXIS_LABEL_PROP_ABS_MT_BLOB_ID,
+ AXIS_LABEL_PROP_ABS_MT_TRACKING_ID,
+ AXIS_LABEL_PROP_ABS_MT_PRESSURE,
+ };
+
+ if (mtdev->caps.slot.maximum > 0)
+ priv->max_touches = mtdev->caps.slot.maximum -
+ mtdev->caps.slot.minimum + 1;
+
+ priv->touch_axes = malloc(priv->num_mt_axes *
+ sizeof(SynapticsTouchAxisRec));
+ if (!priv->touch_axes) {
+ priv->has_touch = FALSE;
+ goto out;
+ }
+
+ axnum = 0;
+ for (i = 0; i < MT_ABS_SIZE; i++) {
+ if (mtdev->caps.has_abs[i]) {
+ switch (i) {
+ /* X and Y axis info is handed by synaptics already, we just
+ * need to map the evdev codes to the valuator numbers */
+ case ABS_MT_POSITION_X - ABS_MT_TOUCH_MAJOR:
+ proto_data->axis_map[i] = 0;
+ break;
+
+ case ABS_MT_POSITION_Y - ABS_MT_TOUCH_MAJOR:
+ proto_data->axis_map[i] = 1;
+ break;
+
+ /* Skip tracking ID info */
+ case ABS_MT_TRACKING_ID - ABS_MT_TOUCH_MAJOR:
+ break;
+
+ default:
+ priv->touch_axes[axnum].label = labels[i];
+ priv->touch_axes[axnum].min = mtdev->caps.abs[i].minimum;
+ priv->touch_axes[axnum].max = mtdev->caps.abs[i].maximum;
+ /* Kernel provides units/mm, X wants units/m */
+ priv->touch_axes[axnum].res =
+ mtdev->caps.abs[i].resolution * 1000;
+ /* Valuators 0-3 are used for X, Y, and scrolling */
+ proto_data->axis_map[i] = 4 + axnum;
+ axnum++;
+ break;
+ }
+ }
+ }
+ }
+
+ out:
+ mtdev_close(mtdev);
+}
+#endif
+
+/**
+ * Probe the open device for dimensions.
+ */
+static void
+EventReadDevDimensions(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ struct eventcomm_proto_data *proto_data = priv->proto_data;
+
+#ifdef HAVE_MULTITOUCH
+ int i;
+#endif
+
+ proto_data = EventProtoDataAlloc();
+ priv->proto_data = proto_data;
+
+#ifdef HAVE_MULTITOUCH
+ for (i = 0; i < MT_ABS_SIZE; i++)
+ proto_data->axis_map[i] = -1;
+ proto_data->cur_slot = -1;
+#endif
+
+ if (event_query_is_touchpad
+ (pInfo->fd, (proto_data) ? proto_data->need_grab : TRUE)) {
+#ifdef HAVE_MULTITOUCH
+ event_query_touch(pInfo);
+#endif
+ event_query_axis_ranges(pInfo);
+ }
+ event_query_model(pInfo->fd, &priv->model, &priv->id_vendor,
+ &priv->id_product);
+
+ xf86IDrvMsg(pInfo, X_PROBED, "Vendor %#hx Product %#hx\n",
+ priv->id_vendor, priv->id_product);
+}
+
+static Bool
+EventAutoDevProbe(InputInfoPtr pInfo, const char *device)
+{
+ /* We are trying to find the right eventX device or fall back to
+ the psaux protocol and the given device from XF86Config */
+ int i;
+ Bool touchpad_found = FALSE;
+ struct dirent **namelist;
+
+ if (device) {
+ int fd = -1;
+
+ SYSCALL(fd = open(device, O_RDONLY));
+ if (fd >= 0) {
+ touchpad_found = event_query_is_touchpad(fd, TRUE);
+
+ SYSCALL(close(fd));
+ /* if a device is set and not a touchpad (or already grabbed),
+ * we must return FALSE. Otherwise, we'll add a device that
+ * wasn't requested for and repeat
+ * f5687a6741a19ef3081e7fd83ac55f6df8bcd5c2. */
+ return touchpad_found;
+ }
+ }
+
+ i = scandir(DEV_INPUT_EVENT, &namelist, EventDevOnly, alphasort);
+ if (i < 0) {
+ xf86IDrvMsg(pInfo, X_ERROR, "Couldn't open %s\n", DEV_INPUT_EVENT);
+ return FALSE;
+ }
+ else if (i == 0) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "The /dev/input/event* device nodes seem to be missing\n");
+ free(namelist);
+ return FALSE;
+ }
+
+ while (i--) {
+ char fname[64];
+ int fd = -1;
+
+ if (!touchpad_found) {
+ sprintf(fname, "%s/%s", DEV_INPUT_EVENT, namelist[i]->d_name);
+ SYSCALL(fd = open(fname, O_RDONLY));
+ if (fd < 0)
+ continue;
+
+ if (event_query_is_touchpad(fd, TRUE)) {
+ touchpad_found = TRUE;
+ xf86IDrvMsg(pInfo, X_PROBED, "auto-dev sets device to %s\n",
+ fname);
+ pInfo->options =
+ xf86ReplaceStrOption(pInfo->options, "Device", fname);
+ }
+ SYSCALL(close(fd));
+ }
+ free(namelist[i]);
+ }
+
+ free(namelist);
+
+ if (!touchpad_found) {
+ xf86IDrvMsg(pInfo, X_ERROR, "no synaptics event device found\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+struct SynapticsProtocolOperations event_proto_operations = {
+ EventDeviceOnHook,
+ EventDeviceOffHook,
+ EventQueryHardware,
+ EventReadHwState,
+ EventAutoDevProbe,
+ EventReadDevDimensions
+};
diff --git a/src/eventcomm.h b/src/eventcomm.h
new file mode 100644
index 0000000..30c09ee
--- /dev/null
+++ b/src/eventcomm.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2004 Peter Osterlund
+ *
+ * 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 Osterlund (petero2@telia.com)
+ */
+
+#ifndef _EVENTCOMM_H_
+#define _EVENTCOMM_H_
+
+#include <linux/input.h>
+#include <linux/version.h>
+#include <xf86Xinput.h>
+#include "synproto.h"
+
+/* for auto-dev: */
+#define DEV_INPUT_EVENT "/dev/input"
+#define EVENT_DEV_NAME "event"
+
+struct eventcomm_proto_data;
+
+extern struct eventcomm_proto_data *EventProtoDataAlloc(void);
+
+extern Bool
+
+EventReadHwState(InputInfoPtr pInfo,
+ struct CommData *comm, struct SynapticsHwState *hwRet);
+
+#endif /* _EVENTCOMM_H_ */
diff --git a/src/properties.c b/src/properties.c
new file mode 100644
index 0000000..3b3f1c5
--- /dev/null
+++ b/src/properties.c
@@ -0,0 +1,844 @@
+/*
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <xorg-server.h>
+#include "xf86Module.h"
+
+#include <X11/Xatom.h>
+#include <xf86.h>
+#include <xf86Xinput.h>
+#include <exevents.h>
+
+#include "synaptics.h"
+#include "synapticsstr.h"
+#include "synaptics-properties.h"
+
+#ifndef XATOM_FLOAT
+#define XATOM_FLOAT "FLOAT"
+#endif
+
+#ifndef XI_PROP_PRODUCT_ID
+#define XI_PROP_PRODUCT_ID "Device Product ID"
+#endif
+
+#ifndef XI_PROP_DEVICE_NODE
+#define XI_PROP_DEVICE_NODE "Device Node"
+#endif
+
+static Atom float_type;
+
+Atom prop_edges = 0;
+Atom prop_finger = 0;
+Atom prop_tap_time = 0;
+Atom prop_tap_move = 0;
+Atom prop_tap_durations = 0;
+Atom prop_clickpad = 0;
+Atom prop_tap_fast = 0;
+Atom prop_middle_timeout = 0;
+Atom prop_twofinger_pressure = 0;
+Atom prop_twofinger_width = 0;
+Atom prop_scrolldist = 0;
+Atom prop_scrolledge = 0;
+Atom prop_scrolltwofinger = 0;
+Atom prop_speed = 0;
+Atom prop_edgemotion_pressure = 0;
+Atom prop_edgemotion_speed = 0;
+Atom prop_edgemotion_always = 0;
+Atom prop_buttonscroll = 0;
+Atom prop_buttonscroll_repeat = 0;
+Atom prop_buttonscroll_time = 0;
+Atom prop_off = 0;
+Atom prop_lockdrags = 0;
+Atom prop_lockdrags_time = 0;
+Atom prop_tapaction = 0;
+Atom prop_clickaction = 0;
+Atom prop_circscroll = 0;
+Atom prop_circscroll_dist = 0;
+Atom prop_circscroll_trigger = 0;
+Atom prop_circpad = 0;
+Atom prop_palm = 0;
+Atom prop_palm_dim = 0;
+Atom prop_coastspeed = 0;
+Atom prop_pressuremotion = 0;
+Atom prop_pressuremotion_factor = 0;
+Atom prop_grab = 0;
+Atom prop_gestures = 0;
+Atom prop_capabilities = 0;
+Atom prop_resolution = 0;
+Atom prop_area = 0;
+Atom prop_softbutton_areas = 0;
+Atom prop_noise_cancellation = 0;
+Atom prop_product_id = 0;
+Atom prop_device_node = 0;
+
+static Atom
+InitTypedAtom(DeviceIntPtr dev, char *name, Atom type, int format, int nvalues,
+ int *values)
+{
+ int i;
+ Atom atom;
+ uint8_t val_8[9]; /* we never have more than 9 values in an atom */
+ uint16_t val_16[9];
+ uint32_t val_32[9];
+ pointer converted;
+
+ for (i = 0; i < nvalues; i++) {
+ switch (format) {
+ case 8:
+ val_8[i] = values[i];
+ break;
+ case 16:
+ val_16[i] = values[i];
+ break;
+ case 32:
+ val_32[i] = values[i];
+ break;
+ }
+ }
+
+ switch (format) {
+ case 8:
+ converted = val_8;
+ break;
+ case 16:
+ converted = val_16;
+ break;
+ case 32:
+ converted = val_32;
+ break;
+ }
+
+ atom = MakeAtom(name, strlen(name), TRUE);
+ XIChangeDeviceProperty(dev, atom, type, format, PropModeReplace, nvalues,
+ converted, FALSE);
+ XISetDevicePropertyDeletable(dev, atom, FALSE);
+ return atom;
+}
+
+static Atom
+InitAtom(DeviceIntPtr dev, char *name, int format, int nvalues, int *values)
+{
+ return InitTypedAtom(dev, name, XA_INTEGER, format, nvalues, values);
+}
+
+static Atom
+InitFloatAtom(DeviceIntPtr dev, char *name, int nvalues, float *values)
+{
+ Atom atom;
+
+ atom = MakeAtom(name, strlen(name), TRUE);
+ XIChangeDeviceProperty(dev, atom, float_type, 32, PropModeReplace,
+ nvalues, values, FALSE);
+ XISetDevicePropertyDeletable(dev, atom, FALSE);
+ return atom;
+}
+
+static void
+InitSoftButtonProperty(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ SynapticsParameters *para = &priv->synpara;
+ int values[8];
+
+ values[0] = para->softbutton_areas[0][0];
+ values[1] = para->softbutton_areas[0][1];
+ values[2] = para->softbutton_areas[0][2];
+ values[3] = para->softbutton_areas[0][3];
+ values[4] = para->softbutton_areas[1][0];
+ values[5] = para->softbutton_areas[1][1];
+ values[6] = para->softbutton_areas[1][2];
+ values[7] = para->softbutton_areas[1][3];
+ prop_softbutton_areas =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 8, values);
+}
+
+void
+InitDeviceProperties(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ SynapticsParameters *para = &priv->synpara;
+ int values[9]; /* we never have more than 9 values in an atom */
+ float fvalues[4]; /* never have more than 4 float values */
+
+ float_type = XIGetKnownProperty(XATOM_FLOAT);
+ if (!float_type) {
+ float_type = MakeAtom(XATOM_FLOAT, strlen(XATOM_FLOAT), TRUE);
+ if (!float_type) {
+ xf86IDrvMsg(pInfo, X_ERROR, "Failed to init float atom. "
+ "Disabling property support.\n");
+ return;
+ }
+ }
+
+ values[0] = para->left_edge;
+ values[1] = para->right_edge;
+ values[2] = para->top_edge;
+ values[3] = para->bottom_edge;
+
+ prop_edges = InitAtom(pInfo->dev, SYNAPTICS_PROP_EDGES, 32, 4, values);
+
+ values[0] = para->finger_low;
+ values[1] = para->finger_high;
+ values[2] = para->finger_press;
+
+ prop_finger = InitAtom(pInfo->dev, SYNAPTICS_PROP_FINGER, 32, 3, values);
+ prop_tap_time =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_TIME, 32, 1, &para->tap_time);
+ prop_tap_move =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_MOVE, 32, 1, &para->tap_move);
+
+ values[0] = para->single_tap_timeout;
+ values[1] = para->tap_time_2;
+ values[2] = para->click_time;
+
+ prop_tap_durations =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_DURATIONS, 32, 3, values);
+ prop_clickpad =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_CLICKPAD, 8, 1, &para->clickpad);
+ prop_tap_fast =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_FAST, 8, 1, &para->fast_taps);
+ prop_middle_timeout =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_MIDDLE_TIMEOUT, 32, 1,
+ &para->emulate_mid_button_time);
+ prop_twofinger_pressure =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_TWOFINGER_PRESSURE, 32, 1,
+ &para->emulate_twofinger_z);
+ prop_twofinger_width =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_TWOFINGER_WIDTH, 32, 1,
+ &para->emulate_twofinger_w);
+
+ values[0] = para->scroll_dist_vert;
+ values[1] = para->scroll_dist_horiz;
+ prop_scrolldist =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_SCROLL_DISTANCE, 32, 2, values);
+
+ values[0] = para->scroll_edge_vert;
+ values[1] = para->scroll_edge_horiz;
+ values[2] = para->scroll_edge_corner;
+ prop_scrolledge =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_SCROLL_EDGE, 8, 3, values);
+ values[0] = para->scroll_twofinger_vert;
+ values[1] = para->scroll_twofinger_horiz;
+ prop_scrolltwofinger =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_SCROLL_TWOFINGER, 8, 2, values);
+
+ fvalues[0] = para->min_speed;
+ fvalues[1] = para->max_speed;
+ fvalues[2] = para->accl;
+ fvalues[3] = para->trackstick_speed;
+ prop_speed = InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_SPEED, 4, fvalues);
+
+ values[0] = para->edge_motion_min_z;
+ values[1] = para->edge_motion_max_z;
+ prop_edgemotion_pressure =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_EDGEMOTION_PRESSURE, 32, 2, values);
+
+ values[0] = para->edge_motion_min_speed;
+ values[1] = para->edge_motion_max_speed;
+ prop_edgemotion_speed =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_EDGEMOTION_SPEED, 32, 2, values);
+ prop_edgemotion_always =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_EDGEMOTION, 8, 1,
+ &para->edge_motion_use_always);
+
+ if (priv->has_scrollbuttons) {
+ values[0] = para->updown_button_scrolling;
+ values[1] = para->leftright_button_scrolling;
+ prop_buttonscroll =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_BUTTONSCROLLING, 8, 2, values);
+
+ values[0] = para->updown_button_repeat;
+ values[1] = para->leftright_button_repeat;
+ prop_buttonscroll_repeat =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_BUTTONSCROLLING_REPEAT, 8, 2,
+ values);
+ prop_buttonscroll_time =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_BUTTONSCROLLING_TIME, 32, 1,
+ &para->scroll_button_repeat);
+ }
+
+ prop_off =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_OFF, 8, 1, &para->touchpad_off);
+ prop_lockdrags =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_LOCKED_DRAGS, 8, 1,
+ &para->locked_drags);
+ prop_lockdrags_time =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_LOCKED_DRAGS_TIMEOUT, 32, 1,
+ &para->locked_drag_time);
+
+ memcpy(values, para->tap_action, MAX_TAP * sizeof(int));
+ prop_tapaction =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_ACTION, 8, MAX_TAP, values);
+
+ memcpy(values, para->click_action, MAX_CLICK * sizeof(int));
+ prop_clickaction =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_CLICK_ACTION, 8, MAX_CLICK, values);
+
+ prop_circscroll =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_CIRCULAR_SCROLLING, 8, 1,
+ &para->circular_scrolling);
+
+ fvalues[0] = para->scroll_dist_circ;
+ prop_circscroll_dist =
+ InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_CIRCULAR_SCROLLING_DIST, 1,
+ fvalues);
+
+ prop_circscroll_trigger =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_CIRCULAR_SCROLLING_TRIGGER, 8, 1,
+ &para->circular_trigger);
+ prop_circpad =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_CIRCULAR_PAD, 8, 1,
+ &para->circular_pad);
+ prop_palm =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_PALM_DETECT, 8, 1,
+ &para->palm_detect);
+
+ values[0] = para->palm_min_width;
+ values[1] = para->palm_min_z;
+
+ prop_palm_dim =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_PALM_DIMENSIONS, 32, 2, values);
+
+ fvalues[0] = para->coasting_speed;
+ fvalues[1] = para->coasting_friction;
+ prop_coastspeed =
+ InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_COASTING_SPEED, 2, fvalues);
+
+ values[0] = para->press_motion_min_z;
+ values[1] = para->press_motion_max_z;
+ prop_pressuremotion =
+ InitTypedAtom(pInfo->dev, SYNAPTICS_PROP_PRESSURE_MOTION, XA_CARDINAL,
+ 32, 2, values);
+
+ fvalues[0] = para->press_motion_min_factor;
+ fvalues[1] = para->press_motion_max_factor;
+
+ prop_pressuremotion_factor =
+ InitFloatAtom(pInfo->dev, SYNAPTICS_PROP_PRESSURE_MOTION_FACTOR, 2,
+ fvalues);
+
+ prop_grab =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_GRAB, 8, 1,
+ &para->grab_event_device);
+
+ values[0] = para->tap_and_drag_gesture;
+ prop_gestures = InitAtom(pInfo->dev, SYNAPTICS_PROP_GESTURES, 8, 1, values);
+
+ values[0] = priv->has_left;
+ values[1] = priv->has_middle;
+ values[2] = priv->has_right;
+ values[3] = priv->has_double;
+ values[4] = priv->has_triple;
+ values[5] = priv->has_pressure;
+ values[6] = priv->has_width;
+ prop_capabilities =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_CAPABILITIES, 8, 7, values);
+
+ values[0] = para->resolution_vert;
+ values[1] = para->resolution_horiz;
+ prop_resolution =
+ InitAtom(pInfo->dev, SYNAPTICS_PROP_RESOLUTION, 32, 2, values);
+
+ values[0] = para->area_left_edge;
+ values[1] = para->area_right_edge;
+ values[2] = para->area_top_edge;
+ values[3] = para->area_bottom_edge;
+ prop_area = InitAtom(pInfo->dev, SYNAPTICS_PROP_AREA, 32, 4, values);
+
+ if (para->clickpad)
+ InitSoftButtonProperty(pInfo);
+
+ values[0] = para->hyst_x;
+ values[1] = para->hyst_y;
+ prop_noise_cancellation = InitAtom(pInfo->dev,
+ SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 2,
+ values);
+
+ /* only init product_id property if we actually know them */
+ if (priv->id_vendor || priv->id_product) {
+ values[0] = priv->id_vendor;
+ values[1] = priv->id_product;
+ prop_product_id =
+ InitAtom(pInfo->dev, XI_PROP_PRODUCT_ID, 32, 2, values);
+ }
+
+ if (priv->device) {
+ prop_device_node =
+ MakeAtom(XI_PROP_DEVICE_NODE, strlen(XI_PROP_DEVICE_NODE), TRUE);
+ XIChangeDeviceProperty(pInfo->dev, prop_device_node, XA_STRING, 8,
+ PropModeReplace, strlen(priv->device),
+ (pointer) priv->device, FALSE);
+ XISetDevicePropertyDeletable(pInfo->dev, prop_device_node, FALSE);
+ }
+
+}
+
+int
+SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
+ BOOL checkonly)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ SynapticsParameters *para = &priv->synpara;
+ SynapticsParameters tmp;
+
+ /* If checkonly is set, no parameters may be changed. So just let the code
+ * change temporary variables and forget about it. */
+ if (checkonly) {
+ tmp = *para;
+ para = &tmp;
+ }
+
+ if (property == prop_edges) {
+ INT32 *edges;
+
+ if (prop->size != 4 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ edges = (INT32 *) prop->data;
+ if (edges[0] > edges[1] || edges[2] > edges[3])
+ return BadValue;
+
+ para->left_edge = edges[0];
+ para->right_edge = edges[1];
+ para->top_edge = edges[2];
+ para->bottom_edge = edges[3];
+
+ }
+ else if (property == prop_finger) {
+ INT32 *finger;
+
+ if (prop->size != 3 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ finger = (INT32 *) prop->data;
+ if (finger[0] > finger[1])
+ return BadValue;
+
+ para->finger_low = finger[0];
+ para->finger_high = finger[1];
+ para->finger_press = finger[2];
+
+ }
+ else if (property == prop_tap_time) {
+ if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->tap_time = *(INT32 *) prop->data;
+
+ }
+ else if (property == prop_tap_move) {
+ if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->tap_move = *(INT32 *) prop->data;
+ }
+ else if (property == prop_tap_durations) {
+ INT32 *timeouts;
+
+ if (prop->size != 3 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ timeouts = (INT32 *) prop->data;
+
+ para->single_tap_timeout = timeouts[0];
+ para->tap_time_2 = timeouts[1];
+ para->click_time = timeouts[2];
+ }
+ else if (property == prop_clickpad) {
+ BOOL value;
+
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ value = *(BOOL *) prop->data;
+ if (!para->clickpad && value && !prop_softbutton_areas)
+ InitSoftButtonProperty(pInfo);
+ else if (para->clickpad && !value && prop_softbutton_areas) {
+ XIDeleteDeviceProperty(dev, prop_softbutton_areas, FALSE);
+ prop_softbutton_areas = 0;
+ }
+
+ para->clickpad = *(BOOL *) prop->data;
+ }
+ else if (property == prop_tap_fast) {
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->fast_taps = *(BOOL *) prop->data;
+
+ }
+ else if (property == prop_middle_timeout) {
+ if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->emulate_mid_button_time = *(INT32 *) prop->data;
+ }
+ else if (property == prop_twofinger_pressure) {
+ if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->emulate_twofinger_z = *(INT32 *) prop->data;
+ }
+ else if (property == prop_twofinger_width) {
+ if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->emulate_twofinger_w = *(INT32 *) prop->data;
+ }
+ else if (property == prop_scrolldist) {
+ INT32 *dist;
+
+ if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ dist = (INT32 *) prop->data;
+ if (dist[0] == 0 || dist[1] == 0)
+ return BadValue;
+
+ if (para->scroll_dist_vert != dist[0]) {
+ para->scroll_dist_vert = dist[0];
+#ifdef HAVE_SMOOTH_SCROLL
+ SetScrollValuator(dev, priv->scroll_axis_vert, SCROLL_TYPE_VERTICAL,
+ para->scroll_dist_vert, 0);
+#endif
+ }
+ if (para->scroll_dist_horiz != dist[1]) {
+ para->scroll_dist_horiz = dist[1];
+#ifdef HAVE_SMOOTH_SCROLL
+ SetScrollValuator(dev, priv->scroll_axis_horiz,
+ SCROLL_TYPE_HORIZONTAL, para->scroll_dist_horiz,
+ 0);
+#endif
+ }
+ }
+ else if (property == prop_scrolledge) {
+ CARD8 *edge;
+
+ if (prop->size != 3 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ edge = (BOOL *) prop->data;
+ para->scroll_edge_vert = edge[0];
+ para->scroll_edge_horiz = edge[1];
+ para->scroll_edge_corner = edge[2];
+ }
+ else if (property == prop_scrolltwofinger) {
+ CARD8 *twofinger;
+
+ if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ twofinger = (BOOL *) prop->data;
+ para->scroll_twofinger_vert = twofinger[0];
+ para->scroll_twofinger_horiz = twofinger[1];
+ }
+ else if (property == prop_speed) {
+ float *speed;
+
+ if (prop->size != 4 || prop->format != 32 || prop->type != float_type)
+ return BadMatch;
+
+ speed = (float *) prop->data;
+ para->min_speed = speed[0];
+ para->max_speed = speed[1];
+ para->accl = speed[2];
+ para->trackstick_speed = speed[3];
+
+ }
+ else if (property == prop_edgemotion_pressure) {
+ CARD32 *pressure;
+
+ if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ pressure = (CARD32 *) prop->data;
+ if (pressure[0] > pressure[1])
+ return BadValue;
+
+ para->edge_motion_min_z = pressure[0];
+ para->edge_motion_max_z = pressure[1];
+
+ }
+ else if (property == prop_edgemotion_speed) {
+ CARD32 *speed;
+
+ if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ speed = (CARD32 *) prop->data;
+ if (speed[0] > speed[1])
+ return BadValue;
+
+ para->edge_motion_min_speed = speed[0];
+ para->edge_motion_max_speed = speed[1];
+
+ }
+ else if (property == prop_edgemotion_always) {
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->edge_motion_use_always = *(BOOL *) prop->data;
+
+ }
+ else if (property == prop_buttonscroll) {
+ BOOL *scroll;
+
+ if (!priv->has_scrollbuttons)
+ return BadMatch;
+
+ if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ scroll = (BOOL *) prop->data;
+ para->updown_button_scrolling = scroll[0];
+ para->leftright_button_scrolling = scroll[1];
+
+ }
+ else if (property == prop_buttonscroll_repeat) {
+ BOOL *repeat;
+
+ if (!priv->has_scrollbuttons)
+ return BadMatch;
+
+ if (prop->size != 2 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ repeat = (BOOL *) prop->data;
+ para->updown_button_repeat = repeat[0];
+ para->leftright_button_repeat = repeat[1];
+ }
+ else if (property == prop_buttonscroll_time) {
+ if (!priv->has_scrollbuttons)
+ return BadMatch;
+
+ if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->scroll_button_repeat = *(INT32 *) prop->data;
+
+ }
+ else if (property == prop_off) {
+ CARD8 off;
+
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ off = *(CARD8 *) prop->data;
+
+ if (off > 2)
+ return BadValue;
+
+ para->touchpad_off = off;
+ }
+ else if (property == prop_gestures) {
+ BOOL *gestures;
+
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ gestures = (BOOL *) prop->data;
+ para->tap_and_drag_gesture = gestures[0];
+ }
+ else if (property == prop_lockdrags) {
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->locked_drags = *(BOOL *) prop->data;
+ }
+ else if (property == prop_lockdrags_time) {
+ if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->locked_drag_time = *(INT32 *) prop->data;
+ }
+ else if (property == prop_tapaction) {
+ int i;
+ CARD8 *action;
+
+ if (prop->size > MAX_TAP || prop->format != 8 ||
+ prop->type != XA_INTEGER)
+ return BadMatch;
+
+ action = (CARD8 *) prop->data;
+
+ for (i = 0; i < MAX_TAP; i++)
+ para->tap_action[i] = action[i];
+ }
+ else if (property == prop_clickaction) {
+ int i;
+ CARD8 *action;
+
+ if (prop->size > MAX_CLICK || prop->format != 8 ||
+ prop->type != XA_INTEGER)
+ return BadMatch;
+
+ action = (CARD8 *) prop->data;
+
+ for (i = 0; i < MAX_CLICK; i++)
+ para->click_action[i] = action[i];
+ }
+ else if (property == prop_circscroll) {
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->circular_scrolling = *(BOOL *) prop->data;
+
+ }
+ else if (property == prop_circscroll_dist) {
+ float circdist;
+
+ if (prop->size != 1 || prop->format != 32 || prop->type != float_type)
+ return BadMatch;
+
+ circdist = *(float *) prop->data;
+ if (circdist == 0)
+ return BadValue;
+
+ para->scroll_dist_circ = circdist;
+ }
+ else if (property == prop_circscroll_trigger) {
+ int trigger;
+
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ trigger = *(CARD8 *) prop->data;
+ if (trigger > 8)
+ return BadValue;
+
+ para->circular_trigger = trigger;
+
+ }
+ else if (property == prop_circpad) {
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->circular_pad = *(BOOL *) prop->data;
+ }
+ else if (property == prop_palm) {
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->palm_detect = *(BOOL *) prop->data;
+ }
+ else if (property == prop_palm_dim) {
+ INT32 *dim;
+
+ if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ dim = (INT32 *) prop->data;
+
+ para->palm_min_width = dim[0];
+ para->palm_min_z = dim[1];
+ }
+ else if (property == prop_coastspeed) {
+ float *coast_speeds;
+
+ if (prop->size != 2 || prop->format != 32 || prop->type != float_type)
+ return BadMatch;
+
+ coast_speeds = (float *) prop->data;
+ para->coasting_speed = coast_speeds[0];
+ para->coasting_friction = coast_speeds[1];
+ }
+ else if (property == prop_pressuremotion) {
+ CARD32 *press;
+
+ if (prop->size != 2 || prop->format != 32 || prop->type != XA_CARDINAL)
+ return BadMatch;
+
+ press = (CARD32 *) prop->data;
+ if (press[0] > press[1])
+ return BadValue;
+
+ para->press_motion_min_z = press[0];
+ para->press_motion_max_z = press[1];
+ }
+ else if (property == prop_grab) {
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ para->grab_event_device = *(BOOL *) prop->data;
+ }
+ else if (property == prop_capabilities) {
+ /* read-only */
+ return BadValue;
+ }
+ else if (property == prop_resolution) {
+ /* read-only */
+ return BadValue;
+ }
+ else if (property == prop_area) {
+ INT32 *area;
+
+ if (prop->size != 4 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ area = (INT32 *) prop->data;
+ if ((((area[0] != 0) && (area[1] != 0)) && (area[0] > area[1])) ||
+ (((area[2] != 0) && (area[3] != 0)) && (area[2] > area[3])))
+ return BadValue;
+
+ para->area_left_edge = area[0];
+ para->area_right_edge = area[1];
+ para->area_top_edge = area[2];
+ para->area_bottom_edge = area[3];
+ }
+ else if (property == prop_softbutton_areas) {
+ int *areas;
+
+ if (prop->size != 8 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ areas = (int *) prop->data;
+ if (!SynapticsIsSoftButtonAreasValid(areas))
+ return BadValue;
+
+ memcpy(para->softbutton_areas[0], areas, 4 * sizeof(int));
+ memcpy(para->softbutton_areas[1], areas + 4, 4 * sizeof(int));
+ }
+ else if (property == prop_noise_cancellation) {
+ INT32 *hyst;
+
+ if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ hyst = (INT32 *) prop->data;
+ if (hyst[0] < 0 || hyst[1] < 0)
+ return BadValue;
+ para->hyst_x = hyst[0];
+ para->hyst_y = hyst[1];
+ }
+ else if (property == prop_product_id || property == prop_device_node)
+ return BadValue; /* read-only */
+
+ return Success;
+}
diff --git a/src/ps2comm.c b/src/ps2comm.c
new file mode 100644
index 0000000..f88b5cb
--- /dev/null
+++ b/src/ps2comm.c
@@ -0,0 +1,673 @@
+/*
+ * Copyright © 1997 C. Scott Ananian
+ * Copyright © 1998-2000 Bruce Kalk
+ * Copyright © 2001 Stefan Gmeiner
+ * Copyright © 2002 Linuxcare Inc. David Kennedy
+ * Copyright © 2003 Fred Hucht <fred@thp.Uni-Duisburg.de>
+ *
+ * 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:
+ * Stefan Gmeiner (riddlebox@freesurf.ch)
+ * C. Scott Ananian (cananian@alumni.priceton.edu)
+ * Bruce Kalk (kall@compass.com)
+ * Linuxcare Inc. David Kennedy (dkennedy@linuxcare.com)
+ * Fred Hucht (fred@thp.Uni-Duisburg.de)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <xorg-server.h>
+#include "synproto.h"
+#include "synaptics.h"
+#include "synapticsstr.h"
+#include "ps2comm.h"
+#include <xf86.h>
+
+#define MAX_UNSYNC_PACKETS 10 /* i.e. 10 to 60 bytes */
+/*
+ * The x/y limits are taken from the Synaptics TouchPad interfacing Guide,
+ * section 2.3.2, which says that they should be valid regardless of the
+ * actual size of the sensor.
+ */
+#define XMIN_NOMINAL 1472
+#define XMAX_NOMINAL 5472
+#define YMIN_NOMINAL 1408
+#define YMAX_NOMINAL 4448
+
+#define XMAX_VALID 6143
+
+/* synaptics queries */
+#define SYN_QUE_IDENTIFY 0x00
+#define SYN_QUE_MODES 0x01
+#define SYN_QUE_CAPABILITIES 0x02
+#define SYN_QUE_MODEL 0x03
+#define SYN_QUE_SERIAL_NUMBER_PREFIX 0x06
+#define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07
+#define SYN_QUE_RESOLUTION 0x08
+#define SYN_QUE_EXT_CAPAB 0x09
+
+/* status request response bits (PS2_CMD_STATUS_REQUEST) */
+#define PS2_RES_REMOTE(r) ((r) & (1 << 22))
+#define PS2_RES_ENABLE(r) ((r) & (1 << 21))
+#define PS2_RES_SCALING(r) ((r) & (1 << 20))
+#define PS2_RES_LEFT(r) ((r) & (1 << 18))
+#define PS2_RES_MIDDLE(r) ((r) & (1 << 17))
+#define PS2_RES_RIGHT(r) ((r) & (1 << 16))
+#define PS2_RES_RESOLUTION(r) (((r) >> 8) & 0x03)
+#define PS2_RES_SAMPLE_RATE(r) ((r) & 0xff)
+
+#ifdef DEBUG
+#define PS2DBG(x) (x)
+#else
+#define PS2DBG(x)
+#endif
+
+/*****************************************************************************
+ * PS/2 Utility functions.
+ * Many parts adapted from tpconfig.c by C. Scott Ananian
+ ****************************************************************************/
+
+/*
+ * Read a byte from the ps/2 port
+ */
+static Bool
+ps2_getbyte(int fd, byte * b)
+{
+ if (xf86WaitForInput(fd, 50000) > 0) {
+ if (xf86ReadSerial(fd, b, 1) != 1) {
+ PS2DBG(ErrorF("ps2_getbyte: No byte read\n"));
+ return FALSE;
+ }
+ PS2DBG(ErrorF("ps2_getbyte: byte %02X read\n", *b));
+ return TRUE;
+ }
+ PS2DBG(ErrorF("ps2_getbyte: timeout xf86WaitForInput\n"));
+ return FALSE;
+}
+
+/*
+ * Write a byte to the ps/2 port, wait for ACK
+ */
+Bool
+ps2_putbyte(int fd, byte b)
+{
+ byte ack;
+
+ if (xf86WriteSerial(fd, &b, 1) != 1) {
+ PS2DBG(ErrorF("ps2_putbyte: error xf86WriteSerial\n"));
+ return FALSE;
+ }
+ PS2DBG(ErrorF("ps2_putbyte: byte %02X send\n", b));
+ /* wait for an ACK */
+ if (!ps2_getbyte(fd, &ack)) {
+ return FALSE;
+ }
+ if (ack != PS2_ACK) {
+ PS2DBG(ErrorF("ps2_putbyte: wrong acknowledge 0x%02x\n", ack));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * Use the Synaptics extended ps/2 syntax to write a special command byte. Needed by
+ * ps2_send_cmd and ps2_set_mode.
+ * special command: 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu
+ * is the command. A 0xF3 or 0xE9 must follow (see ps2_send_cmd, ps2_set_mode)
+ */
+static Bool
+ps2_special_cmd(int fd, byte cmd)
+{
+ int i;
+
+ /* initialize with 'inert' command */
+ if (!ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1))
+ return FALSE;
+
+ /* send 4x 2-bits with set resolution command */
+ for (i = 0; i < 4; i++) {
+ if (!ps2_putbyte(fd, PS2_CMD_SET_RESOLUTION) ||
+ !ps2_putbyte(fd, (cmd >> 6) & 0x3))
+ return FALSE;
+ cmd <<= 2;
+ }
+ return TRUE;
+}
+
+/*
+ * Send a command to the synpatics touchpad by special commands
+ */
+static Bool
+ps2_send_cmd(int fd, byte c)
+{
+ PS2DBG(ErrorF("send command: 0x%02X\n", c));
+ return (ps2_special_cmd(fd, c) && ps2_putbyte(fd, PS2_CMD_STATUS_REQUEST));
+}
+
+/*****************************************************************************
+ * Synaptics communications functions
+ ****************************************************************************/
+
+/*
+ * Set the synaptics touchpad mode byte by special commands
+ */
+static Bool
+ps2_synaptics_set_mode(int fd, byte mode)
+{
+ PS2DBG(ErrorF("set mode byte to: 0x%02X\n", mode));
+ return (ps2_special_cmd(fd, mode) &&
+ ps2_putbyte(fd, PS2_CMD_SET_SAMPLE_RATE) && ps2_putbyte(fd, 0x14));
+}
+
+/*
+ * reset the touchpad
+ */
+static Bool
+ps2_synaptics_reset(int fd)
+{
+ byte r[2];
+
+ xf86FlushInput(fd);
+ PS2DBG(ErrorF("Reset the Touchpad...\n"));
+ if (!ps2_putbyte(fd, PS2_CMD_RESET)) {
+ PS2DBG(ErrorF("...failed\n"));
+ return FALSE;
+ }
+ xf86WaitForInput(fd, 4000000);
+ if (ps2_getbyte(fd, &r[0]) && ps2_getbyte(fd, &r[1])) {
+ if (r[0] == 0xAA && r[1] == 0x00) {
+ PS2DBG(ErrorF("...done\n"));
+ return TRUE;
+ }
+ else {
+ PS2DBG(ErrorF
+ ("...failed. Wrong reset ack 0x%02x, 0x%02x\n", r[0], r[1]));
+ return FALSE;
+ }
+ }
+ PS2DBG(ErrorF("...failed\n"));
+ return FALSE;
+}
+
+/*
+ * Read the model-id bytes from the touchpad
+ * see also SYN_MODEL_* macros
+ */
+static Bool
+ps2_synaptics_model_id(int fd, struct PS2SynapticsHwInfo *synhw)
+{
+ byte mi[3];
+
+ PS2DBG(ErrorF("Read mode id...\n"));
+
+ synhw->model_id = 0;
+ if (ps2_send_cmd(fd, SYN_QUE_MODEL) &&
+ ps2_getbyte(fd, &mi[0]) &&
+ ps2_getbyte(fd, &mi[1]) && ps2_getbyte(fd, &mi[2])) {
+ synhw->model_id = (mi[0] << 16) | (mi[1] << 8) | mi[2];
+ PS2DBG(ErrorF("model-id %06X\n", synhw->model_id));
+ PS2DBG(ErrorF("...done.\n"));
+ return TRUE;
+ }
+ PS2DBG(ErrorF("...failed.\n"));
+ return FALSE;
+}
+
+/*
+ * Read the capability-bits from the touchpad
+ * see also the SYN_CAP_* macros
+ */
+static Bool
+ps2_synaptics_capability(int fd, struct PS2SynapticsHwInfo *synhw)
+{
+ byte cap[3];
+
+ PS2DBG(ErrorF("Read capabilites...\n"));
+
+ synhw->capabilities = 0;
+ synhw->ext_cap = 0;
+ if (ps2_send_cmd(fd, SYN_QUE_CAPABILITIES) &&
+ ps2_getbyte(fd, &cap[0]) &&
+ ps2_getbyte(fd, &cap[1]) && ps2_getbyte(fd, &cap[2])) {
+ synhw->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+ PS2DBG(ErrorF("capabilities %06X\n", synhw->capabilities));
+ if (SYN_CAP_VALID(synhw)) {
+ if (SYN_EXT_CAP_REQUESTS(synhw)) {
+ if (ps2_send_cmd(fd, SYN_QUE_EXT_CAPAB) &&
+ ps2_getbyte(fd, &cap[0]) &&
+ ps2_getbyte(fd, &cap[1]) && ps2_getbyte(fd, &cap[2])) {
+ synhw->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+ PS2DBG(ErrorF("ext-capability %06X\n", synhw->ext_cap));
+ }
+ else {
+ PS2DBG(ErrorF
+ ("synaptics says, that it has extended-capabilities, "
+ "but I cannot read them."));
+ }
+ }
+ PS2DBG(ErrorF("...done.\n"));
+ return TRUE;
+ }
+ }
+ PS2DBG(ErrorF("...failed.\n"));
+ return FALSE;
+}
+
+/*
+ * Identify Touchpad
+ * See also the SYN_ID_* macros
+ */
+static Bool
+ps2_synaptics_identify(int fd, struct PS2SynapticsHwInfo *synhw)
+{
+ byte id[3];
+
+ PS2DBG(ErrorF("Identify Touchpad...\n"));
+
+ synhw->identity = 0;
+ if (ps2_send_cmd(fd, SYN_QUE_IDENTIFY) &&
+ ps2_getbyte(fd, &id[0]) &&
+ ps2_getbyte(fd, &id[1]) && ps2_getbyte(fd, &id[2])) {
+ synhw->identity = (id[0] << 16) | (id[1] << 8) | id[2];
+ PS2DBG(ErrorF("ident %06X\n", synhw->identity));
+ if (SYN_ID_IS_SYNAPTICS(synhw)) {
+ PS2DBG(ErrorF("...done.\n"));
+ return TRUE;
+ }
+ }
+ PS2DBG(ErrorF("...failed.\n"));
+ return FALSE;
+}
+
+static Bool
+ps2_synaptics_enable_device(int fd)
+{
+ return ps2_putbyte(fd, PS2_CMD_ENABLE);
+}
+
+static Bool
+ps2_synaptics_disable_device(int fd)
+{
+ xf86FlushInput(fd);
+ return ps2_putbyte(fd, PS2_CMD_DISABLE);
+}
+
+static Bool
+ps2_query_is_synaptics(InputInfoPtr pInfo, int fd,
+ struct PS2SynapticsHwInfo *synhw)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (ps2_synaptics_disable_device(fd))
+ break;
+ }
+
+ xf86WaitForInput(fd, 20000);
+ xf86FlushInput(fd);
+ if (ps2_synaptics_identify(fd, synhw)) {
+ return TRUE;
+ }
+ else {
+ xf86IDrvMsg(pInfo, X_ERROR, "Query no Synaptics: %06X\n",
+ synhw->identity);
+ return FALSE;
+ }
+}
+
+void
+ps2_print_ident(InputInfoPtr pInfo, const struct PS2SynapticsHwInfo *synhw)
+{
+ xf86IDrvMsg(pInfo, X_PROBED, " Synaptics Touchpad, model: %d\n",
+ SYN_ID_MODEL(synhw));
+ xf86IDrvMsg(pInfo, X_PROBED, " Firmware: %d.%d\n", SYN_ID_MAJOR(synhw),
+ SYN_ID_MINOR(synhw));
+
+ if (SYN_MODEL_ROT180(synhw))
+ xf86IDrvMsg(pInfo, X_PROBED, " 180 degree mounted touchpad\n");
+ if (SYN_MODEL_PORTRAIT(synhw))
+ xf86IDrvMsg(pInfo, X_PROBED, " portrait touchpad\n");
+ xf86IDrvMsg(pInfo, X_PROBED, " Sensor: %d\n", SYN_MODEL_SENSOR(synhw));
+ if (SYN_MODEL_NEWABS(synhw))
+ xf86IDrvMsg(pInfo, X_PROBED, " new absolute packet format\n");
+ if (SYN_MODEL_PEN(synhw))
+ xf86IDrvMsg(pInfo, X_PROBED, " pen detection\n");
+
+ if (SYN_CAP_EXTENDED(synhw)) {
+ xf86IDrvMsg(pInfo, X_PROBED,
+ " Touchpad has extended capability bits\n");
+ if (SYN_CAP_MULTI_BUTTON_NO(synhw))
+ xf86IDrvMsg(pInfo, X_PROBED,
+ " -> %d multi buttons, i.e. besides standard buttons\n",
+ (int) (SYN_CAP_MULTI_BUTTON_NO(synhw)));
+ if (SYN_CAP_MIDDLE_BUTTON(synhw))
+ xf86IDrvMsg(pInfo, X_PROBED, " -> middle button\n");
+ if (SYN_CAP_FOUR_BUTTON(synhw))
+ xf86IDrvMsg(pInfo, X_PROBED, " -> four buttons\n");
+ if (SYN_CAP_MULTIFINGER(synhw))
+ xf86IDrvMsg(pInfo, X_PROBED, " -> multifinger detection\n");
+ if (SYN_CAP_PALMDETECT(synhw))
+ xf86IDrvMsg(pInfo, X_PROBED, " -> palm detection\n");
+ if (SYN_CAP_PASSTHROUGH(synhw))
+ xf86IDrvMsg(pInfo, X_PROBED, " -> pass-through port\n");
+ }
+}
+
+static Bool
+PS2DeviceOffHook(InputInfoPtr pInfo)
+{
+ ps2_synaptics_reset(pInfo->fd);
+ ps2_synaptics_enable_device(pInfo->fd);
+
+ return TRUE;
+}
+
+static Bool
+PS2QueryHardware(InputInfoPtr pInfo)
+{
+ int mode;
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ struct PS2SynapticsHwInfo *synhw;
+
+ if (!priv->proto_data)
+ priv->proto_data = calloc(1, sizeof(struct PS2SynapticsHwInfo));
+ synhw = (struct PS2SynapticsHwInfo *) priv->proto_data;
+
+ /* is the synaptics touchpad active? */
+ if (!ps2_query_is_synaptics(pInfo, pInfo->fd, synhw))
+ return FALSE;
+
+ xf86IDrvMsg(pInfo, X_PROBED, "synaptics touchpad found\n");
+
+ if (!ps2_synaptics_reset(pInfo->fd))
+ xf86IDrvMsg(pInfo, X_ERROR, "reset failed\n");
+
+ if (!ps2_synaptics_identify(pInfo->fd, synhw))
+ return FALSE;
+
+ if (!ps2_synaptics_model_id(pInfo->fd, synhw))
+ return FALSE;
+
+ if (!ps2_synaptics_capability(pInfo->fd, synhw))
+ return FALSE;
+
+ mode = SYN_BIT_ABSOLUTE_MODE | SYN_BIT_HIGH_RATE;
+ if (SYN_ID_MAJOR(synhw) >= 4)
+ mode |= SYN_BIT_DISABLE_GESTURE;
+ if (SYN_CAP_EXTENDED(synhw))
+ mode |= SYN_BIT_W_MODE;
+ if (!ps2_synaptics_set_mode(pInfo->fd, mode))
+ return FALSE;
+
+ ps2_synaptics_enable_device(pInfo->fd);
+
+ ps2_print_ident(pInfo, synhw);
+
+ return TRUE;
+}
+
+/*
+ * Decide if the current packet stored in priv->protoBuf is valid.
+ */
+static Bool
+ps2_packet_ok(struct PS2SynapticsHwInfo *synhw, struct CommData *comm)
+{
+ unsigned char *buf = comm->protoBuf;
+ int newabs = SYN_MODEL_NEWABS(synhw);
+
+ if (newabs ? ((buf[0] & 0xC0) != 0x80) : ((buf[0] & 0xC0) != 0xC0)) {
+ DBG(4, "Synaptics driver lost sync at 1st byte\n");
+ return FALSE;
+ }
+
+ if (!newabs && ((buf[1] & 0x60) != 0x00)) {
+ DBG(4, "Synaptics driver lost sync at 2nd byte\n");
+ return FALSE;
+ }
+
+ if ((newabs ? ((buf[3] & 0xC0) != 0xC0) : ((buf[3] & 0xC0) != 0x80))) {
+ DBG(4, "Synaptics driver lost sync at 4th byte\n");
+ return FALSE;
+ }
+
+ if (!newabs && ((buf[4] & 0x60) != 0x00)) {
+ DBG(4, "Synaptics driver lost sync at 5th byte\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static Bool
+ps2_synaptics_get_packet(InputInfoPtr pInfo, struct PS2SynapticsHwInfo *synhw,
+ struct SynapticsProtocolOperations *proto_ops,
+ struct CommData *comm)
+{
+ int count = 0;
+ int c;
+ unsigned char u;
+
+ while ((c = XisbRead(comm->buffer)) >= 0) {
+ u = (unsigned char) c;
+
+ /* test if there is a reset sequence received */
+ if ((c == 0x00) && (comm->lastByte == 0xAA)) {
+ if (xf86WaitForInput(pInfo->fd, 50000) == 0) {
+ DBG(7, "Reset received\n");
+ proto_ops->QueryHardware(pInfo);
+ }
+ else
+ DBG(3, "faked reset received\n");
+ }
+ comm->lastByte = u;
+
+ /* to avoid endless loops */
+ if (count++ > 30) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Synaptics driver lost sync... got gigantic packet!\n");
+ return FALSE;
+ }
+
+ comm->protoBuf[comm->protoBufTail++] = u;
+
+ /* Check that we have a valid packet. If not, we are out of sync,
+ so we throw away the first byte in the packet. */
+ if (comm->protoBufTail >= 6) {
+ if (!ps2_packet_ok(synhw, comm)) {
+ int i;
+
+ for (i = 0; i < comm->protoBufTail - 1; i++)
+ comm->protoBuf[i] = comm->protoBuf[i + 1];
+ comm->protoBufTail--;
+ comm->outOfSync++;
+ if (comm->outOfSync > MAX_UNSYNC_PACKETS) {
+ comm->outOfSync = 0;
+ DBG(3,
+ "Synaptics synchronization lost too long -> reset touchpad.\n");
+ proto_ops->QueryHardware(pInfo); /* including a reset */
+ continue;
+ }
+ }
+ }
+
+ if (comm->protoBufTail >= 6) { /* Full packet received */
+ if (comm->outOfSync > 0) {
+ comm->outOfSync = 0;
+ DBG(4, "Synaptics driver resynced.\n");
+ }
+ comm->protoBufTail = 0;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+Bool
+PS2ReadHwStateProto(InputInfoPtr pInfo,
+ struct SynapticsProtocolOperations *proto_ops,
+ struct CommData *comm, struct SynapticsHwState *hwRet)
+{
+ unsigned char *buf = comm->protoBuf;
+ struct SynapticsHwState *hw = comm->hwState;
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ SynapticsParameters *para = &priv->synpara;
+ struct PS2SynapticsHwInfo *synhw;
+ int newabs;
+ int w, i;
+
+ synhw = (struct PS2SynapticsHwInfo *) priv->proto_data;
+ if (!synhw) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "PS2ReadHwState, synhw is NULL. This is a bug.\n");
+ return FALSE;
+ }
+
+ newabs = SYN_MODEL_NEWABS(synhw);
+
+ if (!ps2_synaptics_get_packet(pInfo, synhw, proto_ops, comm))
+ return FALSE;
+
+ /* Handle normal packets */
+ hw->x = hw->y = hw->z = hw->numFingers = hw->fingerWidth = 0;
+ hw->left = hw->right = hw->up = hw->down = hw->middle = FALSE;
+ for (i = 0; i < 8; i++)
+ hw->multi[i] = FALSE;
+
+ if (newabs) { /* newer protos... */
+ DBG(7, "using new protocols\n");
+ hw->x = (((buf[3] & 0x10) << 8) | ((buf[1] & 0x0f) << 8) | buf[4]);
+ hw->y = (((buf[3] & 0x20) << 7) | ((buf[1] & 0xf0) << 4) | buf[5]);
+
+ hw->z = buf[2];
+ w = (((buf[0] & 0x30) >> 2) |
+ ((buf[0] & 0x04) >> 1) | ((buf[3] & 0x04) >> 2));
+
+ hw->left = (buf[0] & 0x01) ? 1 : 0;
+ hw->right = (buf[0] & 0x02) ? 1 : 0;
+
+ if (SYN_CAP_EXTENDED(synhw)) {
+ if (SYN_CAP_MIDDLE_BUTTON(synhw)) {
+ hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
+ }
+ if (SYN_CAP_FOUR_BUTTON(synhw)) {
+ hw->up = ((buf[3] & 0x01)) ? 1 : 0;
+ if (hw->left)
+ hw->up = !hw->up;
+ hw->down = ((buf[3] & 0x02)) ? 1 : 0;
+ if (hw->right)
+ hw->down = !hw->down;
+ }
+ if (SYN_CAP_MULTI_BUTTON_NO(synhw)) {
+ if ((buf[3] & 2) ? !hw->right : hw->right) {
+ switch (SYN_CAP_MULTI_BUTTON_NO(synhw) & ~0x01) {
+ default:
+ break;
+ case 8:
+ hw->multi[7] = ((buf[5] & 0x08)) ? 1 : 0;
+ hw->multi[6] = ((buf[4] & 0x08)) ? 1 : 0;
+ case 6:
+ hw->multi[5] = ((buf[5] & 0x04)) ? 1 : 0;
+ hw->multi[4] = ((buf[4] & 0x04)) ? 1 : 0;
+ case 4:
+ hw->multi[3] = ((buf[5] & 0x02)) ? 1 : 0;
+ hw->multi[2] = ((buf[4] & 0x02)) ? 1 : 0;
+ case 2:
+ hw->multi[1] = ((buf[5] & 0x01)) ? 1 : 0;
+ hw->multi[0] = ((buf[4] & 0x01)) ? 1 : 0;
+ }
+ }
+ }
+ }
+ }
+ else { /* old proto... */
+ DBG(7, "using old protocol\n");
+ hw->x = (((buf[1] & 0x1F) << 8) | buf[2]);
+ hw->y = (((buf[4] & 0x1F) << 8) | buf[5]);
+
+ hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F));
+ w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1));
+
+ hw->left = (buf[0] & 0x01) ? 1 : 0;
+ hw->right = (buf[0] & 0x02) ? 1 : 0;
+ }
+
+ hw->y = YMAX_NOMINAL + YMIN_NOMINAL - hw->y;
+
+ if (hw->z >= para->finger_high) {
+ int w_ok = 0;
+
+ /*
+ * Use capability bits to decide if the w value is valid.
+ * If not, set it to 5, which corresponds to a finger of
+ * normal width.
+ */
+ if (SYN_CAP_EXTENDED(synhw)) {
+ if ((w >= 0) && (w <= 1)) {
+ w_ok = SYN_CAP_MULTIFINGER(synhw);
+ }
+ else if (w == 2) {
+ w_ok = SYN_MODEL_PEN(synhw);
+ }
+ else if ((w >= 4) && (w <= 15)) {
+ w_ok = SYN_CAP_PALMDETECT(synhw);
+ }
+ }
+ if (!w_ok)
+ w = 5;
+
+ switch (w) {
+ case 0:
+ hw->numFingers = 2;
+ hw->fingerWidth = 5;
+ break;
+ case 1:
+ hw->numFingers = 3;
+ hw->fingerWidth = 5;
+ break;
+ default:
+ hw->numFingers = 1;
+ hw->fingerWidth = w;
+ break;
+ }
+ }
+ hw->millis = GetTimeInMillis();
+ SynapticsCopyHwState(hwRet, hw);
+ return TRUE;
+}
+
+static Bool
+PS2ReadHwState(InputInfoPtr pInfo,
+ struct CommData *comm, struct SynapticsHwState *hwRet)
+{
+ return PS2ReadHwStateProto(pInfo, &psaux_proto_operations, comm, hwRet);
+}
+
+struct SynapticsProtocolOperations psaux_proto_operations = {
+ NULL,
+ PS2DeviceOffHook,
+ PS2QueryHardware,
+ PS2ReadHwState,
+ NULL,
+ NULL
+};
diff --git a/src/ps2comm.h b/src/ps2comm.h
new file mode 100644
index 0000000..2be55e4
--- /dev/null
+++ b/src/ps2comm.h
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#ifndef _PS2COMM_H_
+#define _PS2COMM_H_
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include "xf86_OSproc.h"
+
+/* acknowledge for commands and parameter */
+#define PS2_ACK 0xFA
+#define PS2_ERROR 0xFC
+
+/* standard PS/2 commands */
+#define PS2_CMD_RESET 0xFF
+#define PS2_CMD_RESEND 0xFE
+#define PS2_CMD_SET_DEFAULT 0xF6
+#define PS2_CMD_DISABLE 0xF5
+#define PS2_CMD_ENABLE 0xF4
+#define PS2_CMD_SET_SAMPLE_RATE 0xF3
+#define PS2_CMD_READ_DEVICE_TYPE 0xF2
+#define PS2_CMD_SET_REMOTE_MODE 0xF0
+#define PS2_CMD_SET_WRAP_MODE 0xEE
+#define PS2_CMD_RESET_WRAP_MODE 0xEC
+#define PS2_CMD_READ_DATA 0xEB
+#define PS2_CMD_SET_STREAM_MODE 0xEA
+#define PS2_CMD_STATUS_REQUEST 0xE9
+#define PS2_CMD_SET_RESOLUTION 0xE8
+#define PS2_CMD_SET_SCALING_2_1 0xE7
+#define PS2_CMD_SET_SCALING_1_1 0xE6
+
+/* synaptics modes */
+#define SYN_BIT_ABSOLUTE_MODE (1 << 7)
+#define SYN_BIT_HIGH_RATE (1 << 6)
+#define SYN_BIT_SLEEP_MODE (1 << 3)
+#define SYN_BIT_DISABLE_GESTURE (1 << 2)
+#define SYN_BIT_W_MODE (1 << 0)
+
+/* synaptics model ID bits */
+#define SYN_MODEL_ROT180(synhw) ((synhw)->model_id & (1 << 23))
+#define SYN_MODEL_PORTRAIT(synhw) ((synhw)->model_id & (1 << 22))
+#define SYN_MODEL_SENSOR(synhw) (((synhw)->model_id >> 16) & 0x3f)
+#define SYN_MODEL_HARDWARE(synhw) (((synhw)->model_id >> 9) & 0x7f)
+#define SYN_MODEL_NEWABS(synhw) ((synhw)->model_id & (1 << 7))
+#define SYN_MODEL_PEN(synhw) ((synhw)->model_id & (1 << 6))
+#define SYN_MODEL_SIMPLIC(synhw) ((synhw)->model_id & (1 << 5))
+#define SYN_MODEL_GEOMETRY(synhw) ((synhw)->model_id & 0x0f)
+
+/* synaptics capability bits */
+#define SYN_CAP_EXTENDED(synhw) ((synhw)->capabilities & (1 << 23))
+#define SYN_CAP_MIDDLE_BUTTON(synhw) ((synhw)->capabilities & (1 << 18))
+#define SYN_CAP_PASSTHROUGH(synhw) ((synhw)->capabilities & (1 << 7))
+#define SYN_CAP_SLEEP(synhw) ((synhw)->capabilities & (1 << 4))
+#define SYN_CAP_FOUR_BUTTON(synhw) ((synhw)->capabilities & (1 << 3))
+#define SYN_CAP_MULTIFINGER(synhw) ((synhw)->capabilities & (1 << 1))
+#define SYN_CAP_PALMDETECT(synhw) ((synhw)->capabilities & (1 << 0))
+#define SYN_CAP_VALID(synhw) ((((synhw)->capabilities & 0x00ff00) >> 8) == 0x47)
+#define SYN_EXT_CAP_REQUESTS(synhw) (((synhw)->capabilities & 0x700000) != 0)
+#define SYN_CAP_MULTI_BUTTON_NO(synhw) (((synhw)->ext_cap & 0x00f000) >> 12)
+
+/* synaptics modes query bits */
+#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
+#define SYN_MODE_RATE(m) ((m) & (1 << 6))
+#define SYN_MODE_BAUD_SLEEP(m) ((m) & (1 << 3))
+#define SYN_MODE_DISABLE_GESTURE(m) ((m) & (1 << 2))
+#define SYN_MODE_PACKSIZE(m) ((m) & (1 << 1))
+#define SYN_MODE_WMODE(m) ((m) & (1 << 0))
+#define SYN_MODE_VALID(m) (((m) & 0xffff00) == 0x3B47)
+
+/* synaptics identify query bits */
+#define SYN_ID_MODEL(synhw) (((synhw)->identity >> 4) & 0x0f)
+#define SYN_ID_MAJOR(synhw) ((synhw)->identity & 0x0f)
+#define SYN_ID_MINOR(synhw) (((synhw)->identity >> 16) & 0xff)
+#define SYN_ID_IS_SYNAPTICS(synhw) ((((synhw)->identity >> 8) & 0xff) == 0x47)
+
+typedef unsigned char byte;
+
+struct PS2SynapticsHwInfo {
+ unsigned int model_id; /* Model-ID */
+ unsigned int capabilities; /* Capabilities */
+ unsigned int ext_cap; /* Extended Capabilities */
+ unsigned int identity; /* Identification */
+};
+
+Bool ps2_putbyte(int fd, byte b);
+void ps2_print_ident(InputInfoPtr pInfo,
+ const struct PS2SynapticsHwInfo *synhw);
+Bool PS2ReadHwStateProto(InputInfoPtr pInfo,
+ struct SynapticsProtocolOperations *proto_ops,
+ struct CommData *comm, struct SynapticsHwState *hwRet);
+
+#endif /* _PS2COMM_H_ */
diff --git a/src/psmcomm.c b/src/psmcomm.c
new file mode 100644
index 0000000..478202a
--- /dev/null
+++ b/src/psmcomm.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright © 1997 C. Scott Ananian
+ * Copyright © 1998-2000 Bruce Kalk
+ * Copyright © 2001 Stefan Gmeiner
+ * Copyright © 2002 Linuxcare Inc. David Kennedy
+ * Copyright © 2003 Fred Hucht
+ * Copyright © 2004 Arne Schwabe
+ *
+ * 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:
+ * Stefan Gmeiner (riddlebox@freesurf.ch)
+ * C. Scott Ananian (cananian@alumni.priceton.edu)
+ * Bruce Kalk (kall@compass.com)
+ * Linuxcare Inc. David Kennedy (dkennedy@linuxcare.com)
+ * Fred Hucht (fred@thp.Uni-Duisburg.de)
+ * Arne Schwabe <schwabe@uni-paderborn.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <xorg-server.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mouse.h>
+#include <errno.h>
+#include <string.h>
+#include "synproto.h"
+#include "synaptics.h"
+#include "synapticsstr.h"
+#include "ps2comm.h" /* ps2_print_ident() */
+#include <xf86.h>
+
+#define SYSCALL(call) while (((call) == -1) && (errno == EINTR))
+
+/*
+ * Identify Touchpad
+ * See also the SYN_ID_* macros
+ */
+static Bool
+psm_synaptics_identify(int fd, synapticshw_t * ident)
+{
+ int ret;
+
+ SYSCALL(ret = ioctl(fd, MOUSE_SYN_GETHWINFO, ident));
+ if (ret == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* This define is used in a ioctl but not in mouse.h :/ */
+#define PSM_LEVEL_NATIVE 2
+
+static Bool
+PSMQueryIsSynaptics(InputInfoPtr pInfo)
+{
+ int ret;
+ int level = PSM_LEVEL_NATIVE;
+ mousehw_t mhw;
+
+ /* Put the device in native protocol mode to be sure
+ * Otherwise HWINFO will not return the right id
+ * And we will need native mode anyway ...
+ */
+ SYSCALL(ret = ioctl(pInfo->fd, MOUSE_SETLEVEL, &level));
+ if (ret != 0) {
+ xf86IDrvMsg(pInfo, X_ERROR, "%s Can't set native mode\n", pInfo->name);
+ return FALSE;
+ }
+ SYSCALL(ret = ioctl(pInfo->fd, MOUSE_GETHWINFO, &mhw));
+ if (ret != 0) {
+ xf86IDrvMsg(pInfo, X_ERROR, "%s Can't get hardware info\n",
+ pInfo->name);
+ return FALSE;
+ }
+
+ if (mhw.model == MOUSE_MODEL_SYNAPTICS) {
+ return TRUE;
+ }
+ else {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "%s Found no Synaptics, found Mouse model %d instead\n",
+ pInfo->name, mhw.model);
+ return FALSE;
+ }
+}
+
+static void
+convert_hw_info(const synapticshw_t * psm_ident,
+ struct PS2SynapticsHwInfo *synhw)
+{
+ memset(synhw, 0, sizeof(*synhw));
+ synhw->model_id = ((psm_ident->infoRot180 << 23) |
+ (psm_ident->infoPortrait << 22) |
+ (psm_ident->infoSensor << 16) |
+ (psm_ident->infoHardware << 9) |
+ (psm_ident->infoNewAbs << 7) |
+ (psm_ident->capPen << 6) |
+ (psm_ident->infoSimplC << 5) |
+ (psm_ident->infoGeometry));
+ synhw->capabilities = ((psm_ident->capExtended << 23) |
+ (psm_ident->capPassthrough << 7) |
+ (psm_ident->capSleep << 4) |
+ (psm_ident->capFourButtons << 3) |
+ (psm_ident->capMultiFinger << 1) |
+ (psm_ident->capPalmDetect));
+ synhw->ext_cap = 0;
+ synhw->identity = ((psm_ident->infoMajor) |
+ (0x47 << 8) | (psm_ident->infoMinor << 16));
+}
+
+static Bool
+PSMQueryHardware(InputInfoPtr pInfo)
+{
+ synapticshw_t psm_ident;
+ struct PS2SynapticsHwInfo *synhw;
+ SynapticsPrivate *priv;
+
+ priv = (SynapticsPrivate *) pInfo->private;
+
+ if (!priv->proto_data)
+ priv->proto_data = calloc(1, sizeof(struct PS2SynapticsHwInfo));
+ synhw = (struct PS2SynapticsHwInfo *) priv->proto_data;
+
+ /* is the synaptics touchpad active? */
+ if (!PSMQueryIsSynaptics(pInfo))
+ return FALSE;
+
+ xf86IDrvMsg(pInfo, X_PROBED, "synaptics touchpad found\n");
+
+ if (!psm_synaptics_identify(pInfo->fd, &psm_ident))
+ return FALSE;
+
+ convert_hw_info(&psm_ident, synhw);
+
+ ps2_print_ident(pInfo, synhw);
+
+ return TRUE;
+}
+
+static Bool
+PSMReadHwState(InputInfoPtr pInfo,
+ struct CommData *comm, struct SynapticsHwState *hwRet)
+{
+ return PS2ReadHwStateProto(pInfo, &psm_proto_operations, comm, hwRet);
+}
+
+struct SynapticsProtocolOperations psm_proto_operations = {
+ NULL,
+ NULL,
+ PSMQueryHardware,
+ PSMReadHwState,
+ NULL,
+ NULL
+};
diff --git a/src/synaptics.c b/src/synaptics.c
new file mode 100644
index 0000000..cd9f936
--- /dev/null
+++ b/src/synaptics.c
@@ -0,0 +1,3529 @@
+/*
+ * Copyright © 1999 Henry Davies
+ * Copyright © 2001 Stefan Gmeiner
+ * Copyright © 2002 S. Lehner
+ * Copyright © 2002 Peter Osterlund
+ * Copyright © 2002 Linuxcare Inc. David Kennedy
+ * Copyright © 2003 Hartwig Felger
+ * Copyright © 2003 Jörg Bösner
+ * Copyright © 2003 Fred Hucht
+ * Copyright © 2004 Alexei Gilchrist
+ * Copyright © 2004 Matthias Ihmig
+ * Copyright © 2006 Stefan Bethge
+ * Copyright © 2006 Christian Thaeter
+ * Copyright © 2007 Joseph P. Skudlarek
+ * Copyright © 2008 Fedor P. Goncharov
+ * Copyright © 2008-2009 Red Hat, Inc.
+ * Copyright © 2011 The Chromium OS Authors
+ *
+ * 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:
+ * Joseph P. Skudlarek <Jskud@Jskud.com>
+ * Christian Thaeter <chth@gmx.net>
+ * Stefan Bethge <stefan.bethge@web.de>
+ * Matthias Ihmig <m.ihmig@gmx.net>
+ * Alexei Gilchrist <alexei@physics.uq.edu.au>
+ * Jörg Bösner <ich@joerg-boesner.de>
+ * Hartwig Felger <hgfelger@hgfelger.de>
+ * Peter Osterlund <petero2@telia.com>
+ * S. Lehner <sam_x@bluemail.ch>
+ * Stefan Gmeiner <riddlebox@freesurf.ch>
+ * Henry Davies <hdavies@ameritech.net> for the
+ * Linuxcare Inc. David Kennedy <dkennedy@linuxcare.com>
+ * Fred Hucht <fred@thp.Uni-Duisburg.de>
+ * Fedor P. Goncharov <fedgo@gorodok.net>
+ * Simon Thum <simon.thum@gmx.de>
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <xorg-server.h>
+#include <unistd.h>
+#include <misc.h>
+#include <xf86.h>
+#include <sys/shm.h>
+#include <math.h>
+#include <stdio.h>
+#include <xf86_OSproc.h>
+#include <xf86Xinput.h>
+#include <exevents.h>
+
+#include <X11/Xatom.h>
+#include <X11/extensions/XI2.h>
+#include <xserver-properties.h>
+#include <ptrveloc.h>
+
+#include "synaptics.h"
+#include "synapticsstr.h"
+#include "synaptics-properties.h"
+
+typedef enum {
+ NO_EDGE = 0,
+ BOTTOM_EDGE = 1,
+ TOP_EDGE = 2,
+ LEFT_EDGE = 4,
+ RIGHT_EDGE = 8,
+ LEFT_BOTTOM_EDGE = BOTTOM_EDGE | LEFT_EDGE,
+ RIGHT_BOTTOM_EDGE = BOTTOM_EDGE | RIGHT_EDGE,
+ RIGHT_TOP_EDGE = TOP_EDGE | RIGHT_EDGE,
+ LEFT_TOP_EDGE = TOP_EDGE | LEFT_EDGE
+} edge_type;
+
+/*
+ * We expect to be receiving a steady 80 packets/sec (which gives 40
+ * reports/sec with more than one finger on the pad, as Advanced Gesture Mode
+ * requires two PS/2 packets per report). Instead of a random scattering of
+ * magic 13 and 20ms numbers scattered throughout the driver, introduce
+ * POLL_MS as 14ms, which is slightly less than 80Hz. 13ms is closer to
+ * 80Hz, but if the kernel event reporting was even slightly delayed,
+ * we would produce synthetic motion followed immediately by genuine
+ * motion, so use 14.
+ *
+ * We use this to call back at a constant rate to at least produce the
+ * illusion of smooth motion. It works a lot better than you'd expect.
+*/
+#define POLL_MS 14
+
+#define MAX(a, b) (((a)>(b))?(a):(b))
+#define MIN(a, b) (((a)<(b))?(a):(b))
+#define TIME_DIFF(a, b) ((int)((a)-(b)))
+
+#define SQR(x) ((x) * (x))
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#ifndef M_SQRT1_2
+#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
+#endif
+
+#define INPUT_BUFFER_SIZE 200
+
+/*****************************************************************************
+ * Forward declaration
+ ****************************************************************************/
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+static int SynapticsPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
+#else
+static InputInfoPtr SynapticsPreInit(InputDriverPtr drv, IDevPtr dev,
+ int flags);
+#endif
+static void SynapticsUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
+static Bool DeviceControl(DeviceIntPtr, int);
+static void ReadInput(InputInfoPtr);
+static int HandleState(InputInfoPtr, struct SynapticsHwState *, CARD32 now,
+ Bool from_timer);
+static int ControlProc(InputInfoPtr, xDeviceCtl *);
+static int SwitchMode(ClientPtr, DeviceIntPtr, int);
+static Bool DeviceInit(DeviceIntPtr);
+static Bool DeviceOn(DeviceIntPtr);
+static Bool DeviceOff(DeviceIntPtr);
+static Bool DeviceClose(DeviceIntPtr);
+static Bool QueryHardware(InputInfoPtr);
+static void ReadDevDimensions(InputInfoPtr);
+static void ScaleCoordinates(SynapticsPrivate * priv,
+ struct SynapticsHwState *hw);
+static void CalculateScalingCoeffs(SynapticsPrivate * priv);
+static void SanitizeDimensions(InputInfoPtr pInfo);
+
+void InitDeviceProperties(InputInfoPtr pInfo);
+int SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
+ BOOL checkonly);
+
+const static struct {
+ const char *name;
+ struct SynapticsProtocolOperations *proto_ops;
+} protocols[] = {
+#ifdef BUILD_EVENTCOMM
+ {
+ "event", &event_proto_operations},
+#endif
+#ifdef BUILD_PSMCOMM
+ {
+ "psm", &psm_proto_operations},
+#endif
+#ifdef BUILD_PS2COMM
+ {
+ "psaux", &psaux_proto_operations}, {
+ "alps", &alps_proto_operations},
+#endif
+ {
+ NULL, NULL}
+};
+
+InputDriverRec SYNAPTICS = {
+ 1,
+ "synaptics",
+ NULL,
+ SynapticsPreInit,
+ SynapticsUnInit,
+ NULL,
+};
+
+static XF86ModuleVersionInfo VersionRec = {
+ "synaptics",
+ 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}
+};
+
+static pointer
+SetupProc(pointer module, pointer options, int *errmaj, int *errmin)
+{
+ xf86AddInputDriver(&SYNAPTICS, module, 0);
+ return module;
+}
+
+_X_EXPORT XF86ModuleData synapticsModuleData = {
+ &VersionRec,
+ &SetupProc,
+ NULL
+};
+
+/*****************************************************************************
+ * Function Definitions
+ ****************************************************************************/
+/**
+ * Fill in default dimensions for backends that cannot query the hardware.
+ * Eventually, we want the edges to be 1900/5400 for x, 1900/4000 for y.
+ * These values are based so that calculate_edge_widths() will give us the
+ * right values.
+ *
+ * The default values 1900, etc. come from the dawn of time, when men where
+ * men, or possibly apes.
+ */
+static void
+SanitizeDimensions(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+
+ if (priv->minx >= priv->maxx) {
+ priv->minx = 1615;
+ priv->maxx = 5685;
+ priv->resx = 0;
+
+ xf86IDrvMsg(pInfo, X_PROBED,
+ "invalid x-axis range. defaulting to %d - %d\n",
+ priv->minx, priv->maxx);
+ }
+
+ if (priv->miny >= priv->maxy) {
+ priv->miny = 1729;
+ priv->maxy = 4171;
+ priv->resy = 0;
+
+ xf86IDrvMsg(pInfo, X_PROBED,
+ "invalid y-axis range. defaulting to %d - %d\n",
+ priv->miny, priv->maxy);
+ }
+
+ if (priv->minp >= priv->maxp) {
+ priv->minp = 0;
+ priv->maxp = 255;
+
+ xf86IDrvMsg(pInfo, X_PROBED,
+ "invalid pressure range. defaulting to %d - %d\n",
+ priv->minp, priv->maxp);
+ }
+
+ if (priv->minw >= priv->maxw) {
+ priv->minw = 0;
+ priv->maxw = 15;
+
+ xf86IDrvMsg(pInfo, X_PROBED,
+ "invalid finger width range. defaulting to %d - %d\n",
+ priv->minw, priv->maxw);
+ }
+}
+
+static Bool
+SetDeviceAndProtocol(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = pInfo->private;
+ char *proto, *device;
+ int i;
+
+ proto = xf86SetStrOption(pInfo->options, "Protocol", NULL);
+ device = xf86SetStrOption(pInfo->options, "Device", NULL);
+
+ /* If proto is auto-dev, unset and let the code do the rest */
+ if (proto && !strcmp(proto, "auto-dev")) {
+ free(proto);
+ proto = NULL;
+ }
+
+ for (i = 0; protocols[i].name; i++) {
+ if ((!device || !proto) &&
+ protocols[i].proto_ops->AutoDevProbe &&
+ protocols[i].proto_ops->AutoDevProbe(pInfo, device))
+ break;
+ else if (proto && !strcmp(proto, protocols[i].name))
+ break;
+ }
+ free(proto);
+ free(device);
+
+ priv->proto_ops = protocols[i].proto_ops;
+
+ return (priv->proto_ops != NULL);
+}
+
+/*
+ * Allocate and initialize read-only memory for the SynapticsParameters data to hold
+ * driver settings.
+ * The function will allocate shared memory if priv->shm_config is TRUE.
+ */
+static Bool
+alloc_shm_data(InputInfoPtr pInfo)
+{
+ int shmid;
+ SynapticsPrivate *priv = pInfo->private;
+
+ if (priv->synshm)
+ return TRUE; /* Already allocated */
+
+ if (priv->shm_config) {
+ if ((shmid = shmget(SHM_SYNAPTICS, 0, 0)) != -1)
+ shmctl(shmid, IPC_RMID, NULL);
+ if ((shmid = shmget(SHM_SYNAPTICS, sizeof(SynapticsSHM),
+ 0774 | IPC_CREAT)) == -1) {
+ xf86IDrvMsg(pInfo, X_ERROR, "error shmget\n");
+ return FALSE;
+ }
+ if ((priv->synshm = (SynapticsSHM *) shmat(shmid, NULL, 0)) == NULL) {
+ xf86IDrvMsg(pInfo, X_ERROR, "error shmat\n");
+ return FALSE;
+ }
+ }
+ else {
+ priv->synshm = calloc(1, sizeof(SynapticsSHM));
+ if (!priv->synshm)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Free SynapticsParameters data previously allocated by alloc_shm_data().
+ */
+static void
+free_shm_data(SynapticsPrivate * priv)
+{
+ int shmid;
+
+ if (!priv->synshm)
+ return;
+
+ if (priv->shm_config) {
+ if ((shmid = shmget(SHM_SYNAPTICS, 0, 0)) != -1)
+ shmctl(shmid, IPC_RMID, NULL);
+ }
+ else {
+ free(priv->synshm);
+ }
+
+ priv->synshm = NULL;
+}
+
+static void
+calculate_edge_widths(SynapticsPrivate * priv, int *l, int *r, int *t, int *b)
+{
+ int width, height;
+ int ewidth, eheight; /* edge width/height */
+
+ width = abs(priv->maxx - priv->minx);
+ height = abs(priv->maxy - priv->miny);
+
+ if (priv->model == MODEL_SYNAPTICS) {
+ ewidth = width * .07;
+ eheight = height * .07;
+ }
+ else if (priv->model == MODEL_ALPS) {
+ ewidth = width * .15;
+ eheight = height * .15;
+ }
+ else if (priv->model == MODEL_APPLETOUCH) {
+ ewidth = width * .085;
+ eheight = height * .085;
+ }
+ else {
+ ewidth = width * .04;
+ eheight = height * .054;
+ }
+
+ *l = priv->minx + ewidth;
+ *r = priv->maxx - ewidth;
+ *t = priv->miny + eheight;
+ *b = priv->maxy - eheight;
+}
+
+static void
+calculate_tap_hysteresis(SynapticsPrivate * priv, int range,
+ int *fingerLow, int *fingerHigh, int *fingerPress)
+{
+ if (priv->model == MODEL_ELANTECH) {
+ /* All Elantech touchpads don't need the Z filtering to get the
+ * number of fingers correctly. See Documentation/elantech.txt
+ * in the kernel.
+ */
+ *fingerLow = priv->minp + 1;
+ *fingerHigh = priv->minp + 1;
+ }
+ else {
+ *fingerLow = priv->minp + range * (25.0 / 256);
+ *fingerHigh = priv->minp + range * (30.0 / 256);
+ }
+
+ *fingerPress = priv->minp + range * 1.000;
+}
+
+/* Area options support both percent values and absolute values. This is
+ * awkward. The xf86Set* calls will print to the log, but they'll
+ * also print an error if we request a percent value but only have an
+ * int. So - check first for percent, then call xf86Set* again to get
+ * the log message.
+ */
+static int
+set_percent_option(pointer options, const char *optname,
+ const int range, const int offset, const int default_value)
+{
+ int result;
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+ double percent = xf86CheckPercentOption(options, optname, -1);
+
+ if (percent >= 0.0) {
+ percent = xf86SetPercentOption(options, optname, -1);
+ result = percent / 100.0 * range + offset;
+ }
+ else
+#endif
+ result = xf86SetIntOption(options, optname, default_value);
+
+ return result;
+}
+
+Bool
+SynapticsIsSoftButtonAreasValid(int *values)
+{
+ Bool right_disabled = FALSE;
+ Bool middle_disabled = FALSE;
+
+ /* Check right button area */
+ if ((((values[0] != 0) && (values[1] != 0)) && (values[0] > values[1])) ||
+ (((values[2] != 0) && (values[3] != 0)) && (values[2] > values[3])))
+ return FALSE;
+
+ /* Check middle button area */
+ if ((((values[4] != 0) && (values[5] != 0)) && (values[4] > values[5])) ||
+ (((values[6] != 0) && (values[7] != 0)) && (values[6] > values[7])))
+ return FALSE;
+
+ if (values[0] == 0 && values[1] == 0 && values[2] == 0 && values[3] == 0)
+ right_disabled = TRUE;
+
+ if (values[4] == 0 && values[5] == 0 && values[6] == 0 && values[7] == 0)
+ middle_disabled = TRUE;
+
+ if (!right_disabled &&
+ ((values[0] && values[0] == values[1]) ||
+ (values[2] && values[2] == values[3])))
+ return FALSE;
+
+ if (!middle_disabled &&
+ ((values[4] && values[4] == values[5]) ||
+ (values[6] && values[6] == values[7])))
+ return FALSE;
+
+ /* Check for overlapping button areas */
+ if (!right_disabled && !middle_disabled) {
+ int right_left = values[0] ? values[0] : INT_MIN;
+ int right_right = values[1] ? values[1] : INT_MAX;
+ int right_top = values[2] ? values[2] : INT_MIN;
+ int right_bottom = values[3] ? values[3] : INT_MAX;
+ int middle_left = values[4] ? values[4] : INT_MIN;
+ int middle_right = values[5] ? values[5] : INT_MAX;
+ int middle_top = values[6] ? values[6] : INT_MIN;
+ int middle_bottom = values[7] ? values[7] : INT_MAX;
+
+ /* If areas overlap in the Y axis */
+ if ((right_bottom <= middle_bottom && right_bottom >= middle_top) ||
+ (right_top <= middle_bottom && right_top >= middle_top)) {
+ /* Check for overlapping left edges */
+ if ((right_left < middle_left && right_right >= middle_left) ||
+ (middle_left < right_left && middle_right >= right_left))
+ return FALSE;
+
+ /* Check for overlapping right edges */
+ if ((right_right > middle_right && right_left <= middle_right) ||
+ (middle_right > right_right && middle_left <= right_right))
+ return FALSE;
+ }
+
+ /* If areas overlap in the X axis */
+ if ((right_left >= middle_left && right_left <= middle_right) ||
+ (right_right >= middle_left && right_right <= middle_right)) {
+ /* Check for overlapping top edges */
+ if ((right_top < middle_top && right_bottom >= middle_top) ||
+ (middle_top < right_top && middle_bottom >= right_top))
+ return FALSE;
+
+ /* Check for overlapping bottom edges */
+ if ((right_bottom > middle_bottom && right_top <= middle_bottom) ||
+ (middle_bottom > right_bottom && middle_top <= right_bottom))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+set_softbutton_areas_option(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = pInfo->private;
+ SynapticsParameters *pars = &priv->synpara;
+ int values[8];
+ int in_percent = 0; /* bitmask for which ones are in % */
+ char *option_string;
+ char *next_num;
+ char *end_str;
+ int i;
+ int width, height;
+
+ if (!pars->clickpad)
+ return;
+
+ option_string = xf86SetStrOption(pInfo->options, "SoftButtonAreas", NULL);
+ if (!option_string)
+ return;
+
+ next_num = option_string;
+
+ for (i = 0; i < 8 && *next_num != '\0'; i++) {
+ long int value = strtol(next_num, &end_str, 0);
+
+ if (value > INT_MAX || value < -INT_MAX)
+ goto fail;
+
+ values[i] = value;
+
+ if (next_num != end_str) {
+ if (end_str && *end_str == '%') {
+ in_percent |= 1 << i;
+ end_str++;
+ }
+ next_num = end_str;
+ }
+ else
+ goto fail;
+ }
+
+ if (i < 8 || *next_num != '\0')
+ goto fail;
+
+ width = priv->maxx - priv->minx;
+ height = priv->maxy - priv->miny;
+
+ for (i = 0; in_percent && i < 8; i++) {
+ int base, size;
+
+ if ((in_percent & (1 << i)) == 0 || values[i] == 0)
+ continue;
+
+ size = ((i % 4) < 2) ? width : height;
+ base = ((i % 4) < 2) ? priv->minx : priv->miny;
+ values[i] = base + size * values[i] / 100.0;
+ }
+
+ if (!SynapticsIsSoftButtonAreasValid(values))
+ goto fail;
+
+ memcpy(pars->softbutton_areas[0], values, 4 * sizeof(int));
+ memcpy(pars->softbutton_areas[1], values + 4, 4 * sizeof(int));
+
+ return;
+
+ fail:
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "invalid SoftButtonAreas value '%s', keeping defaults\n",
+ option_string);
+}
+
+static void
+set_default_parameters(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = pInfo->private; /* read-only */
+ pointer opts = pInfo->options; /* read-only */
+ SynapticsParameters *pars = &priv->synpara; /* modified */
+
+ int horizScrollDelta, vertScrollDelta; /* pixels */
+ int tapMove; /* pixels */
+ int l, r, t, b; /* left, right, top, bottom */
+ int edgeMotionMinSpeed, edgeMotionMaxSpeed; /* pixels/second */
+ double accelFactor; /* 1/pixels */
+ int fingerLow, fingerHigh, fingerPress; /* pressure */
+ int emulateTwoFingerMinZ; /* pressure */
+ int emulateTwoFingerMinW; /* width */
+ int edgeMotionMinZ, edgeMotionMaxZ; /* pressure */
+ int pressureMotionMinZ, pressureMotionMaxZ; /* pressure */
+ int palmMinWidth, palmMinZ; /* pressure */
+ int tapButton1, tapButton2, tapButton3;
+ int clickFinger1, clickFinger2, clickFinger3;
+ Bool vertEdgeScroll, horizEdgeScroll;
+ Bool vertTwoFingerScroll, horizTwoFingerScroll;
+ int horizResolution = 1;
+ int vertResolution = 1;
+ int width, height, diag, range;
+ int horizHyst, vertHyst;
+ int middle_button_timeout;
+
+ /* read the parameters */
+ if (priv->synshm)
+ priv->synshm->version =
+ (PACKAGE_VERSION_MAJOR * 10000 + PACKAGE_VERSION_MINOR * 100 +
+ PACKAGE_VERSION_PATCHLEVEL);
+
+ /* The synaptics specs specify typical edge widths of 4% on x, and 5.4% on
+ * y (page 7) [Synaptics TouchPad Interfacing Guide, 510-000080 - A
+ * Second Edition, http://www.synaptics.com/support/dev_support.cfm, 8 Sep
+ * 2008]. We use 7% for both instead for synaptics devices, and 15% for
+ * ALPS models.
+ * http://bugs.freedesktop.org/show_bug.cgi?id=21214
+ *
+ * If the range was autodetected, apply these edge widths to all four
+ * sides.
+ */
+
+ width = abs(priv->maxx - priv->minx);
+ height = abs(priv->maxy - priv->miny);
+ diag = sqrt(width * width + height * height);
+
+ calculate_edge_widths(priv, &l, &r, &t, &b);
+
+ /* Again, based on typical x/y range and defaults */
+ horizScrollDelta = diag * .020;
+ vertScrollDelta = diag * .020;
+ tapMove = diag * .044;
+ edgeMotionMinSpeed = 1;
+ edgeMotionMaxSpeed = diag * .080;
+ accelFactor = 200.0 / diag; /* trial-and-error */
+
+ /* hysteresis, assume >= 0 is a detected value (e.g. evdev fuzz) */
+ horizHyst = pars->hyst_x >= 0 ? pars->hyst_x : diag * 0.005;
+ vertHyst = pars->hyst_y >= 0 ? pars->hyst_y : diag * 0.005;
+
+ range = priv->maxp - priv->minp + 1;
+
+ calculate_tap_hysteresis(priv, range, &fingerLow, &fingerHigh,
+ &fingerPress);
+
+ /* scaling based on defaults and a pressure of 256 */
+ emulateTwoFingerMinZ = priv->minp + range * (282.0 / 256);
+ edgeMotionMinZ = priv->minp + range * (30.0 / 256);
+ edgeMotionMaxZ = priv->minp + range * (160.0 / 256);
+ pressureMotionMinZ = priv->minp + range * (30.0 / 256);
+ pressureMotionMaxZ = priv->minp + range * (160.0 / 256);
+ palmMinZ = priv->minp + range * (200.0 / 256);
+
+ range = priv->maxw - priv->minw + 1;
+
+ /* scaling based on defaults below and a tool width of 16 */
+ palmMinWidth = priv->minw + range * (10.0 / 16);
+ emulateTwoFingerMinW = priv->minw + range * (7.0 / 16);
+
+ /* Enable tap if we don't have a phys left button */
+ tapButton1 = priv->has_left ? 0 : 1;
+ tapButton2 = priv->has_left ? 0 : 3;
+ tapButton3 = priv->has_left ? 0 : 2;
+
+ /* Enable multifinger-click if only have one physical button,
+ otherwise clickFinger is always button 1. */
+ clickFinger1 = 1;
+ clickFinger2 = (priv->has_right || priv->has_middle) ? 1 : 3;
+ clickFinger3 = (priv->has_right || priv->has_middle) ? 1 : 2;
+
+ /* Enable vert edge scroll if we can't detect doubletap */
+ vertEdgeScroll = priv->has_double ? FALSE : TRUE;
+ horizEdgeScroll = FALSE;
+
+ /* Enable twofinger scroll if we can detect doubletap */
+ vertTwoFingerScroll = priv->has_double ? TRUE : FALSE;
+ horizTwoFingerScroll = FALSE;
+
+ /* Use resolution reported by hardware if available */
+ if ((priv->resx > 0) && (priv->resy > 0)) {
+ horizResolution = priv->resx;
+ vertResolution = priv->resy;
+ }
+
+ /* set the parameters */
+ pars->left_edge = xf86SetIntOption(opts, "LeftEdge", l);
+ pars->right_edge = xf86SetIntOption(opts, "RightEdge", r);
+ pars->top_edge = xf86SetIntOption(opts, "TopEdge", t);
+ pars->bottom_edge = xf86SetIntOption(opts, "BottomEdge", b);
+
+ pars->area_top_edge =
+ set_percent_option(opts, "AreaTopEdge", height, priv->miny, 0);
+ pars->area_bottom_edge =
+ set_percent_option(opts, "AreaBottomEdge", height, priv->miny, 0);
+ pars->area_left_edge =
+ set_percent_option(opts, "AreaLeftEdge", width, priv->minx, 0);
+ pars->area_right_edge =
+ set_percent_option(opts, "AreaRightEdge", width, priv->minx, 0);
+
+ pars->hyst_x =
+ set_percent_option(opts, "HorizHysteresis", width, 0, horizHyst);
+ pars->hyst_y =
+ set_percent_option(opts, "VertHysteresis", height, 0, vertHyst);
+
+ pars->finger_low = xf86SetIntOption(opts, "FingerLow", fingerLow);
+ pars->finger_high = xf86SetIntOption(opts, "FingerHigh", fingerHigh);
+ pars->finger_press = xf86SetIntOption(opts, "FingerPress", fingerPress);
+ pars->tap_time = xf86SetIntOption(opts, "MaxTapTime", 180);
+ pars->tap_move = xf86SetIntOption(opts, "MaxTapMove", tapMove);
+ pars->tap_time_2 = xf86SetIntOption(opts, "MaxDoubleTapTime", 180);
+ pars->click_time = xf86SetIntOption(opts, "ClickTime", 100);
+ pars->clickpad = xf86SetBoolOption(opts, "ClickPad", pars->clickpad); /* Probed */
+ pars->fast_taps = xf86SetBoolOption(opts, "FastTaps", FALSE);
+ /* middle mouse button emulation on a clickpad? nah, you're joking */
+ middle_button_timeout = pars->clickpad ? 0 : 75;
+ pars->emulate_mid_button_time =
+ xf86SetIntOption(opts, "EmulateMidButtonTime", middle_button_timeout);
+ pars->emulate_twofinger_z =
+ xf86SetIntOption(opts, "EmulateTwoFingerMinZ", emulateTwoFingerMinZ);
+ pars->emulate_twofinger_w =
+ xf86SetIntOption(opts, "EmulateTwoFingerMinW", emulateTwoFingerMinW);
+ pars->scroll_dist_vert =
+ xf86SetIntOption(opts, "VertScrollDelta", vertScrollDelta);
+ pars->scroll_dist_horiz =
+ xf86SetIntOption(opts, "HorizScrollDelta", horizScrollDelta);
+ pars->scroll_edge_vert =
+ xf86SetBoolOption(opts, "VertEdgeScroll", vertEdgeScroll);
+ pars->scroll_edge_horiz =
+ xf86SetBoolOption(opts, "HorizEdgeScroll", horizEdgeScroll);
+ pars->scroll_edge_corner = xf86SetBoolOption(opts, "CornerCoasting", FALSE);
+ pars->scroll_twofinger_vert =
+ xf86SetBoolOption(opts, "VertTwoFingerScroll", vertTwoFingerScroll);
+ pars->scroll_twofinger_horiz =
+ xf86SetBoolOption(opts, "HorizTwoFingerScroll", horizTwoFingerScroll);
+ pars->edge_motion_min_z =
+ xf86SetIntOption(opts, "EdgeMotionMinZ", edgeMotionMinZ);
+ pars->edge_motion_max_z =
+ xf86SetIntOption(opts, "EdgeMotionMaxZ", edgeMotionMaxZ);
+ pars->edge_motion_min_speed =
+ xf86SetIntOption(opts, "EdgeMotionMinSpeed", edgeMotionMinSpeed);
+ pars->edge_motion_max_speed =
+ xf86SetIntOption(opts, "EdgeMotionMaxSpeed", edgeMotionMaxSpeed);
+ pars->edge_motion_use_always =
+ xf86SetBoolOption(opts, "EdgeMotionUseAlways", FALSE);
+ if (priv->has_scrollbuttons) {
+ pars->updown_button_scrolling =
+ xf86SetBoolOption(opts, "UpDownScrolling", TRUE);
+ pars->leftright_button_scrolling =
+ xf86SetBoolOption(opts, "LeftRightScrolling", TRUE);
+ pars->updown_button_repeat =
+ xf86SetBoolOption(opts, "UpDownScrollRepeat", TRUE);
+ pars->leftright_button_repeat =
+ xf86SetBoolOption(opts, "LeftRightScrollRepeat", TRUE);
+ }
+ pars->scroll_button_repeat =
+ xf86SetIntOption(opts, "ScrollButtonRepeat", 100);
+ pars->touchpad_off = xf86SetIntOption(opts, "TouchpadOff", 0);
+ pars->locked_drags = xf86SetBoolOption(opts, "LockedDrags", FALSE);
+ pars->locked_drag_time = xf86SetIntOption(opts, "LockedDragTimeout", 5000);
+ pars->tap_action[RT_TAP] = xf86SetIntOption(opts, "RTCornerButton", 0);
+ pars->tap_action[RB_TAP] = xf86SetIntOption(opts, "RBCornerButton", 0);
+ pars->tap_action[LT_TAP] = xf86SetIntOption(opts, "LTCornerButton", 0);
+ pars->tap_action[LB_TAP] = xf86SetIntOption(opts, "LBCornerButton", 0);
+ pars->tap_action[F1_TAP] = xf86SetIntOption(opts, "TapButton1", tapButton1);
+ pars->tap_action[F2_TAP] = xf86SetIntOption(opts, "TapButton2", tapButton2);
+ pars->tap_action[F3_TAP] = xf86SetIntOption(opts, "TapButton3", tapButton3);
+ pars->click_action[F1_CLICK1] =
+ xf86SetIntOption(opts, "ClickFinger1", clickFinger1);
+ pars->click_action[F2_CLICK1] =
+ xf86SetIntOption(opts, "ClickFinger2", clickFinger2);
+ pars->click_action[F3_CLICK1] =
+ xf86SetIntOption(opts, "ClickFinger3", clickFinger3);
+ pars->circular_scrolling =
+ xf86SetBoolOption(opts, "CircularScrolling", FALSE);
+ pars->circular_trigger = xf86SetIntOption(opts, "CircScrollTrigger", 0);
+ pars->circular_pad = xf86SetBoolOption(opts, "CircularPad", FALSE);
+ pars->palm_detect = xf86SetBoolOption(opts, "PalmDetect", FALSE);
+ pars->palm_min_width = xf86SetIntOption(opts, "PalmMinWidth", palmMinWidth);
+ pars->palm_min_z = xf86SetIntOption(opts, "PalmMinZ", palmMinZ);
+ pars->single_tap_timeout = xf86SetIntOption(opts, "SingleTapTimeout", 180);
+ pars->press_motion_min_z =
+ xf86SetIntOption(opts, "PressureMotionMinZ", pressureMotionMinZ);
+ pars->press_motion_max_z =
+ xf86SetIntOption(opts, "PressureMotionMaxZ", pressureMotionMaxZ);
+
+ pars->min_speed = xf86SetRealOption(opts, "MinSpeed", 0.4);
+ pars->max_speed = xf86SetRealOption(opts, "MaxSpeed", 0.7);
+ pars->accl = xf86SetRealOption(opts, "AccelFactor", accelFactor);
+ pars->trackstick_speed = xf86SetRealOption(opts, "TrackstickSpeed", 40);
+ pars->scroll_dist_circ = xf86SetRealOption(opts, "CircScrollDelta", 0.1);
+ pars->coasting_speed = xf86SetRealOption(opts, "CoastingSpeed", 20.0);
+ pars->coasting_friction = xf86SetRealOption(opts, "CoastingFriction", 50);
+ pars->press_motion_min_factor =
+ xf86SetRealOption(opts, "PressureMotionMinFactor", 1.0);
+ pars->press_motion_max_factor =
+ xf86SetRealOption(opts, "PressureMotionMaxFactor", 1.0);
+ pars->grab_event_device = xf86SetBoolOption(opts, "GrabEventDevice", TRUE);
+ pars->tap_and_drag_gesture =
+ xf86SetBoolOption(opts, "TapAndDragGesture", TRUE);
+ pars->resolution_horiz =
+ xf86SetIntOption(opts, "HorizResolution", horizResolution);
+ pars->resolution_vert =
+ xf86SetIntOption(opts, "VertResolution", vertResolution);
+
+ /* Warn about (and fix) incorrectly configured TopEdge/BottomEdge parameters */
+ if (pars->top_edge > pars->bottom_edge) {
+ int tmp = pars->top_edge;
+
+ pars->top_edge = pars->bottom_edge;
+ pars->bottom_edge = tmp;
+ xf86IDrvMsg(pInfo, X_WARNING,
+ "TopEdge is bigger than BottomEdge. Fixing.\n");
+ }
+
+ set_softbutton_areas_option(pInfo);
+}
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 14
+static double
+SynapticsAccelerationProfile(DeviceIntPtr dev,
+ DeviceVelocityPtr vel,
+ double velocity, double thr, double acc)
+{
+#else
+static float
+SynapticsAccelerationProfile(DeviceIntPtr dev,
+ DeviceVelocityPtr vel,
+ float velocity_f, float thr_f, float acc_f)
+{
+ double velocity = velocity_f;
+ double acc = acc_f;
+#endif
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+ SynapticsParameters *para = &priv->synpara;
+
+ double accelfct;
+
+ /*
+ * synaptics accel was originally base on device coordinate based
+ * velocity, which we recover this way so para->accl retains its scale.
+ */
+ velocity /= vel->const_acceleration;
+
+ /* speed up linear with finger velocity */
+ accelfct = velocity * para->accl;
+
+ /* clip acceleration factor */
+ if (accelfct > para->max_speed * acc)
+ accelfct = para->max_speed * acc;
+ else if (accelfct < para->min_speed)
+ accelfct = para->min_speed;
+
+ /* modify speed according to pressure */
+ if (priv->moving_state == MS_TOUCHPAD_RELATIVE) {
+ int minZ = para->press_motion_min_z;
+ int maxZ = para->press_motion_max_z;
+ double minFctr = para->press_motion_min_factor;
+ double maxFctr = para->press_motion_max_factor;
+
+ if (priv->hwState->z <= minZ) {
+ accelfct *= minFctr;
+ }
+ else if (priv->hwState->z >= maxZ) {
+ accelfct *= maxFctr;
+ }
+ else {
+ accelfct *=
+ minFctr + (priv->hwState->z - minZ) * (maxFctr -
+ minFctr) / (maxZ - minZ);
+ }
+ }
+
+ return accelfct;
+}
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12
+static int
+ NewSynapticsPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags);
+
+/*
+ * called by the module loader for initialization
+ */
+static InputInfoPtr
+SynapticsPreInit(InputDriverPtr drv, IDevPtr dev, int flags)
+{
+ InputInfoPtr pInfo;
+
+ /* Allocate a new InputInfoRec and add it to the head xf86InputDevs. */
+ pInfo = xf86AllocateInput(drv, 0);
+ if (!pInfo) {
+ return NULL;
+ }
+
+ /* initialize the InputInfoRec */
+ pInfo->name = dev->identifier;
+ pInfo->reverse_conversion_proc = NULL;
+ pInfo->dev = NULL;
+ pInfo->private_flags = 0;
+ pInfo->flags = XI86_SEND_DRAG_EVENTS;
+ pInfo->conf_idev = dev;
+ pInfo->always_core_feedback = 0;
+
+ xf86CollectInputOptions(pInfo, NULL, NULL);
+
+ if (NewSynapticsPreInit(drv, pInfo, flags) != Success)
+ return NULL;
+
+ pInfo->flags |= XI86_CONFIGURED;
+
+ return pInfo;
+}
+
+static int
+NewSynapticsPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
+#else
+static int
+SynapticsPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
+#endif
+{
+ SynapticsPrivate *priv;
+
+ /* allocate memory for SynapticsPrivateRec */
+ priv = calloc(1, sizeof(SynapticsPrivate));
+ if (!priv)
+ return BadAlloc;
+
+ pInfo->type_name = XI_TOUCHPAD;
+ pInfo->device_control = DeviceControl;
+ pInfo->read_input = ReadInput;
+ pInfo->control_proc = ControlProc;
+ pInfo->switch_mode = SwitchMode;
+ pInfo->private = priv;
+
+ /* allocate now so we don't allocate in the signal handler */
+ priv->timer = TimerSet(NULL, 0, 0, NULL, NULL);
+ if (!priv->timer) {
+ free(priv);
+ return BadAlloc;
+ }
+
+ /* may change pInfo->options */
+ if (!SetDeviceAndProtocol(pInfo)) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Synaptics driver unable to detect protocol\n");
+ goto SetupProc_fail;
+ }
+
+ priv->device = xf86FindOptionValue(pInfo->options, "Device");
+
+ /* open the touchpad device */
+ pInfo->fd = xf86OpenSerial(pInfo->options);
+ if (pInfo->fd == -1) {
+ xf86IDrvMsg(pInfo, X_ERROR, "Synaptics driver unable to open device\n");
+ goto SetupProc_fail;
+ }
+ xf86ErrorFVerb(6, "port opened successfully\n");
+
+ /* initialize variables */
+ priv->repeatButtons = 0;
+ priv->nextRepeat = 0;
+ priv->count_packet_finger = 0;
+ priv->tap_state = TS_START;
+ priv->tap_button = 0;
+ priv->tap_button_state = TBS_BUTTON_UP;
+ priv->touch_on.millis = 0;
+ priv->synpara.hyst_x = -1;
+ priv->synpara.hyst_y = -1;
+
+ /* read hardware dimensions */
+ ReadDevDimensions(pInfo);
+
+ /* install shared memory or normal memory for parameters */
+ priv->shm_config = xf86SetBoolOption(pInfo->options, "SHMConfig", FALSE);
+
+ set_default_parameters(pInfo);
+
+ CalculateScalingCoeffs(priv);
+
+ if (!alloc_shm_data(pInfo))
+ goto SetupProc_fail;
+
+ priv->comm.buffer = XisbNew(pInfo->fd, INPUT_BUFFER_SIZE);
+
+ if (!QueryHardware(pInfo)) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "Unable to query/initialize Synaptics hardware.\n");
+ goto SetupProc_fail;
+ }
+
+ xf86ProcessCommonOptions(pInfo, pInfo->options);
+
+ if (pInfo->fd != -1) {
+ if (priv->comm.buffer) {
+ XisbFree(priv->comm.buffer);
+ priv->comm.buffer = NULL;
+ }
+ xf86CloseSerial(pInfo->fd);
+ }
+ pInfo->fd = -1;
+
+ return Success;
+
+ SetupProc_fail:
+ if (pInfo->fd >= 0) {
+ xf86CloseSerial(pInfo->fd);
+ pInfo->fd = -1;
+ }
+
+ if (priv->comm.buffer)
+ XisbFree(priv->comm.buffer);
+ free_shm_data(priv);
+ free(priv->proto_data);
+ free(priv->timer);
+ free(priv);
+ pInfo->private = NULL;
+ return BadAlloc;
+}
+
+/*
+ * Uninitialize the device.
+ */
+static void
+SynapticsUnInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
+{
+ SynapticsPrivate *priv = ((SynapticsPrivate *) pInfo->private);
+
+ if (priv && priv->timer)
+ free(priv->timer);
+ if (priv && priv->proto_data)
+ free(priv->proto_data);
+#ifdef HAVE_SMOOTH_SCROLL
+ if (priv && priv->scroll_events_mask)
+ valuator_mask_free(&priv->scroll_events_mask);
+#endif
+#ifdef HAVE_MULTITOUCH
+ if (priv && priv->open_slots)
+ free(priv->open_slots);
+#endif
+ free(pInfo->private);
+ pInfo->private = NULL;
+ xf86DeleteInput(pInfo, 0);
+}
+
+/*
+ * Alter the control parameters for the mouse. Note that all special
+ * protocol values are handled by dix.
+ */
+static void
+SynapticsCtrl(DeviceIntPtr device, PtrCtrl * ctrl)
+{
+}
+
+static Bool
+DeviceControl(DeviceIntPtr dev, int mode)
+{
+ Bool RetValue;
+
+ switch (mode) {
+ case DEVICE_INIT:
+ RetValue = DeviceInit(dev);
+ break;
+ case DEVICE_ON:
+ RetValue = DeviceOn(dev);
+ break;
+ case DEVICE_OFF:
+ RetValue = DeviceOff(dev);
+ break;
+ case DEVICE_CLOSE:
+ RetValue = DeviceClose(dev);
+ break;
+ default:
+ RetValue = BadValue;
+ }
+
+ return RetValue;
+}
+
+static Bool
+DeviceOn(DeviceIntPtr dev)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+
+ DBG(3, "Synaptics DeviceOn called\n");
+
+ pInfo->fd = xf86OpenSerial(pInfo->options);
+ if (pInfo->fd == -1) {
+ xf86IDrvMsg(pInfo, X_WARNING, "cannot open input device\n");
+ return !Success;
+ }
+
+ if (priv->proto_ops->DeviceOnHook &&
+ !priv->proto_ops->DeviceOnHook(pInfo, &priv->synpara))
+ return !Success;
+
+ priv->comm.buffer = XisbNew(pInfo->fd, INPUT_BUFFER_SIZE);
+ if (!priv->comm.buffer) {
+ xf86CloseSerial(pInfo->fd);
+ pInfo->fd = -1;
+ return !Success;
+ }
+
+ xf86FlushInput(pInfo->fd);
+
+ /* reinit the pad */
+ if (!QueryHardware(pInfo)) {
+ XisbFree(priv->comm.buffer);
+ priv->comm.buffer = NULL;
+ xf86CloseSerial(pInfo->fd);
+ pInfo->fd = -1;
+ return !Success;
+ }
+
+ xf86AddEnabledDevice(pInfo);
+ dev->public.on = TRUE;
+
+ return Success;
+}
+
+static void
+SynapticsReset(SynapticsPrivate * priv)
+{
+ SynapticsResetHwState(priv->hwState);
+ SynapticsResetHwState(priv->local_hw_state);
+ SynapticsResetHwState(priv->old_hw_state);
+ SynapticsResetHwState(priv->comm.hwState);
+
+ memset(priv->move_hist, 0, sizeof(priv->move_hist));
+ priv->hyst_center_x = 0;
+ priv->hyst_center_y = 0;
+ memset(&priv->scroll, 0, sizeof(priv->scroll));
+ priv->count_packet_finger = 0;
+ priv->finger_state = FS_UNTOUCHED;
+ priv->last_motion_millis = 0;
+ priv->tap_state = TS_START;
+ priv->tap_button = 0;
+ priv->tap_button_state = TBS_BUTTON_UP;
+ priv->moving_state = MS_FALSE;
+ priv->vert_scroll_edge_on = FALSE;
+ priv->horiz_scroll_edge_on = FALSE;
+ priv->vert_scroll_twofinger_on = FALSE;
+ priv->horiz_scroll_twofinger_on = FALSE;
+ priv->circ_scroll_on = FALSE;
+ priv->circ_scroll_vert = FALSE;
+ priv->mid_emu_state = MBE_OFF;
+ priv->nextRepeat = 0;
+ priv->lastButtons = 0;
+ priv->prev_z = 0;
+ priv->prevFingers = 0;
+#ifdef HAVE_MULTITOUCH
+ memset(priv->open_slots, 0, priv->num_slots * sizeof(int));
+#endif
+}
+
+static Bool
+DeviceOff(DeviceIntPtr dev)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+ Bool rc = Success;
+
+ DBG(3, "Synaptics DeviceOff called\n");
+
+ if (pInfo->fd != -1) {
+ TimerCancel(priv->timer);
+ xf86RemoveEnabledDevice(pInfo);
+ SynapticsReset(priv);
+
+ if (priv->proto_ops->DeviceOffHook &&
+ !priv->proto_ops->DeviceOffHook(pInfo))
+ rc = !Success;
+ if (priv->comm.buffer) {
+ XisbFree(priv->comm.buffer);
+ priv->comm.buffer = NULL;
+ }
+ xf86CloseSerial(pInfo->fd);
+ pInfo->fd = -1;
+ }
+ dev->public.on = FALSE;
+ return rc;
+}
+
+static Bool
+DeviceClose(DeviceIntPtr dev)
+{
+ Bool RetValue;
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+
+ RetValue = DeviceOff(dev);
+ TimerFree(priv->timer);
+ priv->timer = NULL;
+ free_shm_data(priv);
+ SynapticsHwStateFree(&priv->hwState);
+ SynapticsHwStateFree(&priv->old_hw_state);
+ SynapticsHwStateFree(&priv->local_hw_state);
+ SynapticsHwStateFree(&priv->comm.hwState);
+ return RetValue;
+}
+
+static void
+InitAxesLabels(Atom *labels, int nlabels, const SynapticsPrivate * priv)
+{
+#ifdef HAVE_MULTITOUCH
+ int i;
+#endif
+
+ memset(labels, 0, nlabels * sizeof(Atom));
+ switch (nlabels) {
+ default:
+#ifdef HAVE_SMOOTH_SCROLL
+ case 4:
+ labels[3] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_VSCROLL);
+ case 3:
+ labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_HSCROLL);
+#endif
+ case 2:
+ labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
+ case 1:
+ labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
+ break;
+ }
+
+#ifdef HAVE_MULTITOUCH
+ for (i = 0; i < priv->num_mt_axes; i++) {
+ SynapticsTouchAxisRec *axis = &priv->touch_axes[i];
+ int axnum = nlabels - priv->num_mt_axes + i;
+
+ labels[axnum] = XIGetKnownProperty(axis->label);
+ }
+#endif
+}
+
+static void
+InitButtonLabels(Atom *labels, int nlabels)
+{
+ memset(labels, 0, nlabels * sizeof(Atom));
+ switch (nlabels) {
+ default:
+ case 7:
+ labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT);
+ case 6:
+ labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT);
+ case 5:
+ labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN);
+ case 4:
+ labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP);
+ case 3:
+ labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT);
+ case 2:
+ labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE);
+ case 1:
+ labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT);
+ break;
+ }
+}
+
+static void
+DeviceInitTouch(DeviceIntPtr dev, Atom *axes_labels)
+{
+#ifdef HAVE_MULTITOUCH
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+ int i;
+
+ if (priv->has_touch) {
+ priv->num_slots =
+ priv->max_touches ? priv->max_touches : SYNAPTICS_MAX_TOUCHES;
+
+ priv->open_slots = malloc(priv->num_slots * sizeof(int));
+ if (!priv->open_slots) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "failed to allocate open touch slots array\n");
+ priv->has_touch = 0;
+ priv->num_slots = 0;
+ return;
+ }
+
+ /* x/y + whatever other MT axes we found */
+ if (!InitTouchClassDeviceStruct(dev, priv->max_touches,
+ XIDependentTouch,
+ 2 + priv->num_mt_axes)) {
+ xf86IDrvMsg(pInfo, X_ERROR,
+ "failed to initialize touch class device\n");
+ priv->has_touch = 0;
+ priv->num_slots = 0;
+ free(priv->open_slots);
+ priv->open_slots = NULL;
+ return;
+ }
+
+ for (i = 0; i < priv->num_mt_axes; i++) {
+ SynapticsTouchAxisRec *axis = &priv->touch_axes[i];
+ int axnum = 4 + i; /* Skip x, y, and scroll axes */
+
+ if (!xf86InitValuatorAxisStruct(dev, axnum, axes_labels[axnum],
+ axis->min, axis->max, axis->res, 0,
+ axis->res, Absolute)) {
+ xf86IDrvMsg(pInfo, X_WARNING,
+ "failed to initialize axis %s, skipping\n",
+ axis->label);
+ continue;
+ }
+
+ xf86InitValuatorDefaults(dev, axnum);
+ }
+ }
+#endif
+}
+
+static Bool
+DeviceInit(DeviceIntPtr dev)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+ Atom float_type, prop;
+ float tmpf;
+ unsigned char map[SYN_MAX_BUTTONS + 1];
+ int i;
+ int min, max;
+ int num_axes = 2;
+ Atom btn_labels[SYN_MAX_BUTTONS] = { 0 };
+ Atom *axes_labels;
+ DeviceVelocityPtr pVel;
+
+#ifdef HAVE_SMOOTH_SCROLL
+ num_axes += 2;
+#endif
+
+#ifdef HAVE_MULTITOUCH
+ num_axes += priv->num_mt_axes;
+#endif
+
+ axes_labels = calloc(num_axes, sizeof(Atom));
+ if (!axes_labels) {
+ xf86IDrvMsg(pInfo, X_ERROR, "failed to allocate axis labels\n");
+ return !Success;
+ }
+
+ InitAxesLabels(axes_labels, num_axes, priv);
+ InitButtonLabels(btn_labels, SYN_MAX_BUTTONS);
+
+ DBG(3, "Synaptics DeviceInit called\n");
+
+ for (i = 0; i <= SYN_MAX_BUTTONS; i++)
+ map[i] = i;
+
+ dev->public.on = FALSE;
+
+ InitPointerDeviceStruct((DevicePtr) dev, map,
+ SYN_MAX_BUTTONS,
+ btn_labels,
+ SynapticsCtrl,
+ GetMotionHistorySize(), num_axes, axes_labels);
+
+ /*
+ * setup dix acceleration to match legacy synaptics settings, and
+ * etablish a device-specific profile to do stuff like pressure-related
+ * acceleration.
+ */
+ if (NULL != (pVel = GetDevicePredictableAccelData(dev))) {
+ SetDeviceSpecificAccelerationProfile(pVel,
+ SynapticsAccelerationProfile);
+
+ /* float property type */
+ float_type = XIGetKnownProperty(XATOM_FLOAT);
+
+ /* translate MinAcc to constant deceleration.
+ * May be overridden in xf86InitValuatorDefaults */
+ tmpf = 1.0 / priv->synpara.min_speed;
+
+ xf86IDrvMsg(pInfo, X_CONFIG,
+ "(accel) MinSpeed is now constant deceleration " "%.1f\n",
+ tmpf);
+ prop = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
+ XIChangeDeviceProperty(dev, prop, float_type, 32,
+ PropModeReplace, 1, &tmpf, FALSE);
+
+ /* adjust accordingly */
+ priv->synpara.max_speed /= priv->synpara.min_speed;
+ priv->synpara.min_speed = 1.0;
+
+ /* synaptics seems to report 80 packet/s, but dix scales for
+ * 100 packet/s by default. */
+ pVel->corr_mul = 12.5f; /*1000[ms]/80[/s] = 12.5 */
+
+ xf86IDrvMsg(pInfo, X_CONFIG, "MaxSpeed is now %.2f\n",
+ priv->synpara.max_speed);
+ xf86IDrvMsg(pInfo, X_CONFIG, "AccelFactor is now %.3f\n",
+ priv->synpara.accl);
+
+ prop = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
+ i = AccelProfileDeviceSpecific;
+ XIChangeDeviceProperty(dev, prop, XA_INTEGER, 32,
+ PropModeReplace, 1, &i, FALSE);
+ }
+
+ /* X valuator */
+ if (priv->minx < priv->maxx) {
+ min = priv->minx;
+ max = priv->maxx;
+ }
+ else {
+ min = 0;
+ max = -1;
+ }
+
+ xf86InitValuatorAxisStruct(dev, 0, axes_labels[0],
+ min, max, priv->resx * 1000, 0, priv->resx * 1000
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ , Relative
+#endif
+ );
+ xf86InitValuatorDefaults(dev, 0);
+
+ /* Y valuator */
+ if (priv->miny < priv->maxy) {
+ min = priv->miny;
+ max = priv->maxy;
+ }
+ else {
+ min = 0;
+ max = -1;
+ }
+
+ xf86InitValuatorAxisStruct(dev, 1, axes_labels[1],
+ min, max, priv->resy * 1000, 0, priv->resy * 1000
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ , Relative
+#endif
+ );
+ xf86InitValuatorDefaults(dev, 1);
+
+#ifdef HAVE_SMOOTH_SCROLL
+ xf86InitValuatorAxisStruct(dev, 2, axes_labels[2], 0, -1, 0, 0, 0,
+ Relative);
+ priv->scroll_axis_horiz = 2;
+ xf86InitValuatorAxisStruct(dev, 3, axes_labels[3], 0, -1, 0, 0, 0,
+ Relative);
+ priv->scroll_axis_vert = 3;
+ priv->scroll_events_mask = valuator_mask_new(MAX_VALUATORS);
+ if (!priv->scroll_events_mask) {
+ free(axes_labels);
+ return !Success;
+ }
+
+ SetScrollValuator(dev, priv->scroll_axis_horiz, SCROLL_TYPE_HORIZONTAL,
+ priv->synpara.scroll_dist_horiz, 0);
+ SetScrollValuator(dev, priv->scroll_axis_vert, SCROLL_TYPE_VERTICAL,
+ priv->synpara.scroll_dist_vert, 0);
+#endif
+
+ DeviceInitTouch(dev, axes_labels);
+
+ free(axes_labels);
+
+ priv->hwState = SynapticsHwStateAlloc(priv);
+ if (!priv->hwState)
+ goto fail;
+
+ priv->old_hw_state = SynapticsHwStateAlloc(priv);
+ if (!priv->old_hw_state)
+ goto fail;
+
+ priv->local_hw_state = SynapticsHwStateAlloc(priv);
+ if (!priv->local_hw_state)
+ goto fail;
+
+ priv->comm.hwState = SynapticsHwStateAlloc(priv);
+
+ if (!alloc_shm_data(pInfo))
+ goto fail;
+
+ InitDeviceProperties(pInfo);
+ XIRegisterPropertyHandler(pInfo->dev, SetProperty, NULL, NULL);
+
+ return Success;
+
+ fail:
+ free_shm_data(priv);
+ free(priv->local_hw_state);
+ free(priv->hwState);
+#ifdef HAVE_MULTITOUCH
+ free(priv->open_slots);
+#endif
+ return !Success;
+}
+
+/*
+ * Convert from absolute X/Y coordinates to a coordinate system where
+ * -1 corresponds to the left/upper edge and +1 corresponds to the
+ * right/lower edge.
+ */
+static void
+relative_coords(SynapticsPrivate * priv, int x, int y,
+ double *relX, double *relY)
+{
+ int minX = priv->synpara.left_edge;
+ int maxX = priv->synpara.right_edge;
+ int minY = priv->synpara.top_edge;
+ int maxY = priv->synpara.bottom_edge;
+ double xCenter = (minX + maxX) / 2.0;
+ double yCenter = (minY + maxY) / 2.0;
+
+ if ((maxX - xCenter > 0) && (maxY - yCenter > 0)) {
+ *relX = (x - xCenter) / (maxX - xCenter);
+ *relY = (y - yCenter) / (maxY - yCenter);
+ }
+ else {
+ *relX = 0;
+ *relY = 0;
+ }
+}
+
+/* return angle of point relative to center */
+static double
+angle(SynapticsPrivate * priv, int x, int y)
+{
+ double xCenter = (priv->synpara.left_edge + priv->synpara.right_edge) / 2.0;
+ double yCenter = (priv->synpara.top_edge + priv->synpara.bottom_edge) / 2.0;
+
+ return atan2(-(y - yCenter), x - xCenter);
+}
+
+/* return angle difference */
+static double
+diffa(double a1, double a2)
+{
+ double da = fmod(a2 - a1, 2 * M_PI);
+
+ if (da < 0)
+ da += 2 * M_PI;
+ if (da > M_PI)
+ da -= 2 * M_PI;
+ return da;
+}
+
+static edge_type
+circular_edge_detection(SynapticsPrivate * priv, int x, int y)
+{
+ edge_type edge = 0;
+ double relX, relY, relR;
+
+ relative_coords(priv, x, y, &relX, &relY);
+ relR = SQR(relX) + SQR(relY);
+
+ if (relR > 1) {
+ /* we are outside the ellipse enclosed by the edge parameters */
+ if (relX > M_SQRT1_2)
+ edge |= RIGHT_EDGE;
+ else if (relX < -M_SQRT1_2)
+ edge |= LEFT_EDGE;
+
+ if (relY < -M_SQRT1_2)
+ edge |= TOP_EDGE;
+ else if (relY > M_SQRT1_2)
+ edge |= BOTTOM_EDGE;
+ }
+
+ return edge;
+}
+
+static edge_type
+edge_detection(SynapticsPrivate * priv, int x, int y)
+{
+ edge_type edge = NO_EDGE;
+
+ if (priv->synpara.circular_pad)
+ return circular_edge_detection(priv, x, y);
+
+ if (x > priv->synpara.right_edge)
+ edge |= RIGHT_EDGE;
+ else if (x < priv->synpara.left_edge)
+ edge |= LEFT_EDGE;
+
+ if (y < priv->synpara.top_edge)
+ edge |= TOP_EDGE;
+ else if (y > priv->synpara.bottom_edge)
+ edge |= BOTTOM_EDGE;
+
+ return edge;
+}
+
+/* Checks whether coordinates are in the Synaptics Area
+ * or not. If no Synaptics Area is defined (i.e. if
+ * priv->synpara.area_{left|right|top|bottom}_edge are
+ * all set to zero), the function returns TRUE.
+ */
+static Bool
+is_inside_active_area(SynapticsPrivate * priv, int x, int y)
+{
+ Bool inside_area = TRUE;
+
+ if ((priv->synpara.area_left_edge != 0) &&
+ (x < priv->synpara.area_left_edge))
+ inside_area = FALSE;
+ else if ((priv->synpara.area_right_edge != 0) &&
+ (x > priv->synpara.area_right_edge))
+ inside_area = FALSE;
+
+ if ((priv->synpara.area_top_edge != 0) && (y < priv->synpara.area_top_edge))
+ inside_area = FALSE;
+ else if ((priv->synpara.area_bottom_edge != 0) &&
+ (y > priv->synpara.area_bottom_edge))
+ inside_area = FALSE;
+
+ return inside_area;
+}
+
+static Bool
+is_inside_button_area(SynapticsParameters * para, int which, int x, int y)
+{
+ Bool inside_area = TRUE;
+
+ if (para->softbutton_areas[which][0] == 0 &&
+ para->softbutton_areas[which][1] == 0 &&
+ para->softbutton_areas[which][2] == 0 &&
+ para->softbutton_areas[which][3] == 0)
+ return FALSE;
+
+ if (para->softbutton_areas[which][0] &&
+ x < para->softbutton_areas[which][0])
+ inside_area = FALSE;
+ else if (para->softbutton_areas[which][1] &&
+ x > para->softbutton_areas[which][1])
+ inside_area = FALSE;
+ else if (para->softbutton_areas[which][2] &&
+ y < para->softbutton_areas[which][2])
+ inside_area = FALSE;
+ else if (para->softbutton_areas[which][3] &&
+ y > para->softbutton_areas[which][3])
+ inside_area = FALSE;
+
+ return inside_area;
+}
+
+static Bool
+is_inside_rightbutton_area(SynapticsParameters * para, int x, int y)
+{
+ return is_inside_button_area(para, 0, x, y);
+}
+
+static Bool
+is_inside_middlebutton_area(SynapticsParameters * para, int x, int y)
+{
+ return is_inside_button_area(para, 1, x, y);
+}
+
+static CARD32
+timerFunc(OsTimerPtr timer, CARD32 now, pointer arg)
+{
+ InputInfoPtr pInfo = arg;
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+ struct SynapticsHwState *hw = priv->local_hw_state;
+ int delay;
+ int sigstate;
+
+ sigstate = xf86BlockSIGIO();
+
+ priv->hwState->millis += now - priv->timer_time;
+ SynapticsCopyHwState(hw, priv->hwState);
+ SynapticsResetTouchHwState(hw, FALSE);
+ delay = HandleState(pInfo, hw, hw->millis, TRUE);
+
+ priv->timer_time = now;
+ priv->timer = TimerSet(priv->timer, 0, delay, timerFunc, pInfo);
+
+ xf86UnblockSIGIO(sigstate);
+
+ return 0;
+}
+
+static int
+clamp(int val, int min, int max)
+{
+ if (val < min)
+ return min;
+ else if (val < max)
+ return val;
+ else
+ return max;
+}
+
+static Bool
+SynapticsGetHwState(InputInfoPtr pInfo, SynapticsPrivate * priv,
+ struct SynapticsHwState *hw)
+{
+ return priv->proto_ops->ReadHwState(pInfo, &priv->comm, hw);
+}
+
+/*
+ * called for each full received packet from the touchpad
+ */
+static void
+ReadInput(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+ struct SynapticsHwState *hw = priv->local_hw_state;
+ int delay = 0;
+ Bool newDelay = FALSE;
+
+ SynapticsResetTouchHwState(hw, FALSE);
+
+ while (SynapticsGetHwState(pInfo, priv, hw)) {
+ /* Semi-mt device touch slots do not track touches. When there is a
+ * change in the number of touches, we must disregard the temporary
+ * motion changes. */
+ if (priv->has_semi_mt && hw->numFingers != priv->hwState->numFingers) {
+ hw->cumulative_dx = priv->hwState->cumulative_dx;
+ hw->cumulative_dy = priv->hwState->cumulative_dy;
+ }
+
+ /* timer may cause actual events to lag behind (#48777) */
+ if (priv->hwState->millis > hw->millis)
+ hw->millis = priv->hwState->millis;
+
+ SynapticsCopyHwState(priv->hwState, hw);
+ delay = HandleState(pInfo, hw, hw->millis, FALSE);
+ newDelay = TRUE;
+ }
+
+ if (newDelay) {
+ priv->timer_time = GetTimeInMillis();
+ priv->timer = TimerSet(priv->timer, 0, delay, timerFunc, pInfo);
+ }
+}
+
+static int
+HandleMidButtonEmulation(SynapticsPrivate * priv, struct SynapticsHwState *hw,
+ CARD32 now, int *delay)
+{
+ SynapticsParameters *para = &priv->synpara;
+ Bool done = FALSE;
+ int timeleft;
+ int mid = 0;
+
+ if (para->emulate_mid_button_time <= 0)
+ return mid;
+
+ while (!done) {
+ switch (priv->mid_emu_state) {
+ case MBE_LEFT_CLICK:
+ case MBE_RIGHT_CLICK:
+ case MBE_OFF:
+ priv->button_delay_millis = now;
+ if (hw->left) {
+ priv->mid_emu_state = MBE_LEFT;
+ }
+ else if (hw->right) {
+ priv->mid_emu_state = MBE_RIGHT;
+ }
+ else {
+ done = TRUE;
+ }
+ break;
+ case MBE_LEFT:
+ timeleft =
+ TIME_DIFF(priv->button_delay_millis +
+ para->emulate_mid_button_time, now);
+ if (timeleft > 0)
+ *delay = MIN(*delay, timeleft);
+
+ /* timeout, but within the same ReadInput cycle! */
+ if ((timeleft <= 0) && !hw->left) {
+ priv->mid_emu_state = MBE_LEFT_CLICK;
+ done = TRUE;
+ }
+ else if ((!hw->left) || (timeleft <= 0)) {
+ hw->left = TRUE;
+ priv->mid_emu_state = MBE_TIMEOUT;
+ done = TRUE;
+ }
+ else if (hw->right) {
+ priv->mid_emu_state = MBE_MID;
+ }
+ else {
+ hw->left = FALSE;
+ done = TRUE;
+ }
+ break;
+ case MBE_RIGHT:
+ timeleft =
+ TIME_DIFF(priv->button_delay_millis +
+ para->emulate_mid_button_time, now);
+ if (timeleft > 0)
+ *delay = MIN(*delay, timeleft);
+
+ /* timeout, but within the same ReadInput cycle! */
+ if ((timeleft <= 0) && !hw->right) {
+ priv->mid_emu_state = MBE_RIGHT_CLICK;
+ done = TRUE;
+ }
+ else if (!hw->right || (timeleft <= 0)) {
+ hw->right = TRUE;
+ priv->mid_emu_state = MBE_TIMEOUT;
+ done = TRUE;
+ }
+ else if (hw->left) {
+ priv->mid_emu_state = MBE_MID;
+ }
+ else {
+ hw->right = FALSE;
+ done = TRUE;
+ }
+ break;
+ case MBE_MID:
+ if (!hw->left && !hw->right) {
+ priv->mid_emu_state = MBE_OFF;
+ }
+ else {
+ mid = TRUE;
+ hw->left = hw->right = FALSE;
+ done = TRUE;
+ }
+ break;
+ case MBE_TIMEOUT:
+ if (!hw->left && !hw->right) {
+ priv->mid_emu_state = MBE_OFF;
+ }
+ else {
+ done = TRUE;
+ }
+ }
+ }
+ return mid;
+}
+
+static enum FingerState
+SynapticsDetectFinger(SynapticsPrivate * priv, struct SynapticsHwState *hw)
+{
+ SynapticsParameters *para = &priv->synpara;
+ enum FingerState finger;
+
+ /* finger detection thru pressure and threshold */
+ if (hw->z < para->finger_low)
+ return FS_UNTOUCHED;
+
+ if (priv->finger_state == FS_BLOCKED)
+ return FS_BLOCKED;
+
+ if (hw->z > para->finger_press && priv->finger_state < FS_PRESSED)
+ finger = FS_PRESSED;
+ else if (hw->z > para->finger_high && priv->finger_state == FS_UNTOUCHED)
+ finger = FS_TOUCHED;
+ else
+ finger = priv->finger_state;
+
+ if (!para->palm_detect)
+ return finger;
+
+ /* palm detection */
+
+ if ((hw->z > para->palm_min_z) && (hw->fingerWidth > para->palm_min_width))
+ return FS_BLOCKED;
+
+ if (hw->x == 0 || priv->finger_state == FS_UNTOUCHED)
+ priv->avg_width = 0;
+ else
+ priv->avg_width += (hw->fingerWidth - priv->avg_width + 1) / 2;
+
+ if (finger != FS_UNTOUCHED && priv->finger_state == FS_UNTOUCHED) {
+ int safe_width = MAX(hw->fingerWidth, priv->avg_width);
+
+ if (hw->numFingers > 1 || /* more than one finger -> not a palm */
+ ((safe_width < 6) && (priv->prev_z < para->finger_high)) || /* thin finger, distinct touch -> not a palm */
+ ((safe_width < 7) && (priv->prev_z < para->finger_high / 2))) { /* thin finger, distinct touch -> not a palm */
+ /* leave finger value as is */
+ }
+ else if (hw->z > priv->prev_z + 1) /* z not stable, may be a palm */
+ finger = FS_UNTOUCHED;
+ else if (hw->z < priv->prev_z - 5) /* z not stable, may be a palm */
+ finger = FS_UNTOUCHED;
+ else if (hw->fingerWidth > para->palm_min_width) /* finger width too large -> probably palm */
+ finger = FS_UNTOUCHED;
+ }
+ priv->prev_z = hw->z;
+
+ return finger;
+}
+
+static void
+SelectTapButton(SynapticsPrivate * priv, edge_type edge)
+{
+ TapEvent tap;
+
+ if (priv->synpara.touchpad_off == 2) {
+ priv->tap_button = 0;
+ return;
+ }
+
+ switch (priv->tap_max_fingers) {
+ case 1:
+ switch (edge) {
+ case RIGHT_TOP_EDGE:
+ DBG(7, "right top edge\n");
+ tap = RT_TAP;
+ break;
+ case RIGHT_BOTTOM_EDGE:
+ DBG(7, "right bottom edge\n");
+ tap = RB_TAP;
+ break;
+ case LEFT_TOP_EDGE:
+ DBG(7, "left top edge\n");
+ tap = LT_TAP;
+ break;
+ case LEFT_BOTTOM_EDGE:
+ DBG(7, "left bottom edge\n");
+ tap = LB_TAP;
+ break;
+ default:
+ DBG(7, "no edge\n");
+ tap = F1_TAP;
+ break;
+ }
+ break;
+ case 2:
+ DBG(7, "two finger tap\n");
+ tap = F2_TAP;
+ break;
+ case 3:
+ DBG(7, "three finger tap\n");
+ tap = F3_TAP;
+ break;
+ default:
+ priv->tap_button = 0;
+ return;
+ }
+
+ priv->tap_button = priv->synpara.tap_action[tap];
+ priv->tap_button = clamp(priv->tap_button, 0, SYN_MAX_BUTTONS);
+}
+
+static void
+SetTapState(SynapticsPrivate * priv, enum TapState tap_state, CARD32 millis)
+{
+ SynapticsParameters *para = &priv->synpara;
+
+ DBG(3, "SetTapState - %d -> %d (millis:%u)\n", priv->tap_state, tap_state,
+ millis);
+ switch (tap_state) {
+ case TS_START:
+ priv->tap_button_state = TBS_BUTTON_UP;
+ priv->tap_max_fingers = 0;
+ break;
+ case TS_1:
+ priv->tap_button_state = TBS_BUTTON_UP;
+ break;
+ case TS_2A:
+ if (para->fast_taps)
+ priv->tap_button_state = TBS_BUTTON_DOWN;
+ else
+ priv->tap_button_state = TBS_BUTTON_UP;
+ break;
+ case TS_2B:
+ priv->tap_button_state = TBS_BUTTON_UP;
+ break;
+ case TS_3:
+ priv->tap_button_state = TBS_BUTTON_DOWN;
+ break;
+ case TS_SINGLETAP:
+ if (para->fast_taps)
+ priv->tap_button_state = TBS_BUTTON_UP;
+ else
+ priv->tap_button_state = TBS_BUTTON_DOWN;
+ priv->touch_on.millis = millis;
+ break;
+ default:
+ break;
+ }
+ priv->tap_state = tap_state;
+}
+
+static void
+SetMovingState(SynapticsPrivate * priv, enum MovingState moving_state,
+ CARD32 millis)
+{
+ DBG(7, "SetMovingState - %d -> %d center at %d/%d (millis:%u)\n",
+ priv->moving_state, moving_state, priv->hwState->x, priv->hwState->y,
+ millis);
+
+ if (moving_state == MS_TRACKSTICK) {
+ priv->trackstick_neutral_x = priv->hwState->x;
+ priv->trackstick_neutral_y = priv->hwState->y;
+ }
+ priv->moving_state = moving_state;
+}
+
+static int
+GetTimeOut(SynapticsPrivate * priv)
+{
+ SynapticsParameters *para = &priv->synpara;
+
+ switch (priv->tap_state) {
+ case TS_1:
+ case TS_3:
+ case TS_5:
+ return para->tap_time;
+ case TS_SINGLETAP:
+ return para->click_time;
+ case TS_2A:
+ return para->single_tap_timeout;
+ case TS_2B:
+ return para->tap_time_2;
+ case TS_4:
+ return para->locked_drag_time;
+ default:
+ return -1; /* No timeout */
+ }
+}
+
+static int
+HandleTapProcessing(SynapticsPrivate * priv, struct SynapticsHwState *hw,
+ CARD32 now, enum FingerState finger,
+ Bool inside_active_area)
+{
+ SynapticsParameters *para = &priv->synpara;
+ Bool touch, release, is_timeout, move, press;
+ int timeleft, timeout;
+ edge_type edge;
+ int delay = 1000000000;
+
+ if (priv->finger_state == FS_BLOCKED)
+ return delay;
+
+ touch = finger >= FS_TOUCHED && priv->finger_state == FS_UNTOUCHED;
+ release = finger == FS_UNTOUCHED && priv->finger_state >= FS_TOUCHED;
+ move = (finger >= FS_TOUCHED &&
+ (priv->tap_max_fingers <=
+ ((priv->horiz_scroll_twofinger_on ||
+ priv->vert_scroll_twofinger_on) ? 2 : 1)) &&
+ ((abs(hw->x - priv->touch_on.x) >= para->tap_move) ||
+ (abs(hw->y - priv->touch_on.y) >= para->tap_move)));
+ press = (hw->left || hw->right || hw->middle);
+
+ if (touch) {
+ priv->touch_on.x = hw->x;
+ priv->touch_on.y = hw->y;
+ priv->touch_on.millis = now;
+ }
+ else if (release) {
+ priv->touch_on.millis = now;
+ }
+ if (hw->z > para->finger_high)
+ if (priv->tap_max_fingers < hw->numFingers)
+ priv->tap_max_fingers = hw->numFingers;
+ timeout = GetTimeOut(priv);
+ timeleft = TIME_DIFF(priv->touch_on.millis + timeout, now);
+ is_timeout = timeleft <= 0;
+
+ restart:
+ switch (priv->tap_state) {
+ case TS_START:
+ if (touch)
+ SetTapState(priv, TS_1, now);
+ break;
+ case TS_1:
+ if (para->clickpad && press) {
+ SetTapState(priv, TS_CLICKPAD_MOVE, now);
+ goto restart;
+ }
+ if (move) {
+ SetMovingState(priv, MS_TOUCHPAD_RELATIVE, now);
+ SetTapState(priv, TS_MOVE, now);
+ goto restart;
+ }
+ else if (is_timeout) {
+ if (finger == FS_TOUCHED) {
+ SetMovingState(priv, MS_TOUCHPAD_RELATIVE, now);
+ }
+ else if (finger == FS_PRESSED) {
+ SetMovingState(priv, MS_TRACKSTICK, now);
+ }
+ SetTapState(priv, TS_MOVE, now);
+ goto restart;
+ }
+ else if (release) {
+ edge = edge_detection(priv, priv->touch_on.x, priv->touch_on.y);
+ SelectTapButton(priv, edge);
+ /* Disable taps outside of the active area */
+ if (!inside_active_area) {
+ priv->tap_button = 0;
+ }
+ SetTapState(priv, TS_2A, now);
+ }
+ break;
+ case TS_MOVE:
+ if (para->clickpad && press) {
+ SetTapState(priv, TS_CLICKPAD_MOVE, now);
+ goto restart;
+ }
+ if (move && priv->moving_state == MS_TRACKSTICK) {
+ SetMovingState(priv, MS_TOUCHPAD_RELATIVE, now);
+ }
+ if (release) {
+ SetMovingState(priv, MS_FALSE, now);
+ SetTapState(priv, TS_START, now);
+ }
+ break;
+ case TS_2A:
+ if (touch)
+ SetTapState(priv, TS_3, now);
+ else if (is_timeout)
+ SetTapState(priv, TS_SINGLETAP, now);
+ break;
+ case TS_2B:
+ if (touch) {
+ SetTapState(priv, TS_3, now);
+ }
+ else if (is_timeout) {
+ SetTapState(priv, TS_START, now);
+ priv->tap_button_state = TBS_BUTTON_DOWN_UP;
+ }
+ break;
+ case TS_SINGLETAP:
+ if (touch)
+ SetTapState(priv, TS_1, now);
+ else if (is_timeout)
+ SetTapState(priv, TS_START, now);
+ break;
+ case TS_3:
+ if (move) {
+ if (para->tap_and_drag_gesture) {
+ SetMovingState(priv, MS_TOUCHPAD_RELATIVE, now);
+ SetTapState(priv, TS_DRAG, now);
+ }
+ else {
+ SetTapState(priv, TS_1, now);
+ }
+ goto restart;
+ }
+ else if (is_timeout) {
+ if (para->tap_and_drag_gesture) {
+ if (finger == FS_TOUCHED) {
+ SetMovingState(priv, MS_TOUCHPAD_RELATIVE, now);
+ }
+ else if (finger == FS_PRESSED) {
+ SetMovingState(priv, MS_TRACKSTICK, now);
+ }
+ SetTapState(priv, TS_DRAG, now);
+ }
+ else {
+ SetTapState(priv, TS_1, now);
+ }
+ goto restart;
+ }
+ else if (release) {
+ SetTapState(priv, TS_2B, now);
+ }
+ break;
+ case TS_DRAG:
+ if (para->clickpad && press) {
+ SetTapState(priv, TS_CLICKPAD_MOVE, now);
+ goto restart;
+ }
+ if (move)
+ SetMovingState(priv, MS_TOUCHPAD_RELATIVE, now);
+ if (release) {
+ SetMovingState(priv, MS_FALSE, now);
+ if (para->locked_drags) {
+ SetTapState(priv, TS_4, now);
+ }
+ else {
+ SetTapState(priv, TS_START, now);
+ }
+ }
+ break;
+ case TS_4:
+ if (is_timeout) {
+ SetTapState(priv, TS_START, now);
+ goto restart;
+ }
+ if (touch)
+ SetTapState(priv, TS_5, now);
+ break;
+ case TS_5:
+ if (is_timeout || move) {
+ SetTapState(priv, TS_DRAG, now);
+ goto restart;
+ }
+ else if (release) {
+ SetMovingState(priv, MS_FALSE, now);
+ SetTapState(priv, TS_START, now);
+ }
+ break;
+ case TS_CLICKPAD_MOVE:
+ /* Disable scrolling once a button is pressed on a clickpad */
+ priv->vert_scroll_edge_on = FALSE;
+ priv->horiz_scroll_edge_on = FALSE;
+ priv->vert_scroll_twofinger_on = FALSE;
+ priv->horiz_scroll_twofinger_on = FALSE;
+
+ /* Assume one touch is only for holding the clickpad button down */
+ if (hw->numFingers > 1)
+ hw->numFingers--;
+ SetMovingState(priv, MS_TOUCHPAD_RELATIVE, now);
+ if (!press) {
+ SetMovingState(priv, MS_FALSE, now);
+ SetTapState(priv, TS_MOVE, now);
+ priv->count_packet_finger = 0;
+ }
+ break;
+ }
+
+ timeout = GetTimeOut(priv);
+ if (timeout >= 0) {
+ timeleft = TIME_DIFF(priv->touch_on.millis + timeout, now);
+ delay = clamp(timeleft, 1, delay);
+ }
+ return delay;
+}
+
+#define HIST(a) (priv->move_hist[((priv->hist_index - (a) + SYNAPTICS_MOVE_HISTORY) % SYNAPTICS_MOVE_HISTORY)])
+#define HIST_DELTA(a, b, e) ((HIST((a)).e) - (HIST((b)).e))
+
+static void
+store_history(SynapticsPrivate * priv, int x, int y, CARD32 millis)
+{
+ int idx = (priv->hist_index + 1) % SYNAPTICS_MOVE_HISTORY;
+
+ priv->move_hist[idx].x = x;
+ priv->move_hist[idx].y = y;
+ priv->move_hist[idx].millis = millis;
+ priv->hist_index = idx;
+ if (priv->count_packet_finger < SYNAPTICS_MOVE_HISTORY)
+ priv->count_packet_finger++;
+}
+
+/*
+ * Estimate the slope for the data sequence [x3, x2, x1, x0] by using
+ * linear regression to fit a line to the data and use the slope of the
+ * line.
+ */
+static double
+estimate_delta(double x0, double x1, double x2, double x3)
+{
+ return x0 * 0.3 + x1 * 0.1 - x2 * 0.1 - x3 * 0.3;
+}
+
+/**
+ * Applies hysteresis. center is shifted such that it is in range with
+ * in by the margin again. The new center is returned.
+ * @param in the current value
+ * @param center the current center
+ * @param margin the margin to center in which no change is applied
+ * @return the new center (which might coincide with the previous)
+ */
+static int
+hysteresis(int in, int center, int margin)
+{
+ int diff = in - center;
+
+ if (abs(diff) <= margin) {
+ diff = 0;
+ }
+ else if (diff > margin) {
+ diff -= margin;
+ }
+ else if (diff < -margin) {
+ diff += margin;
+ }
+ return center + diff;
+}
+
+static void
+get_delta_for_trackstick(SynapticsPrivate * priv,
+ const struct SynapticsHwState *hw, double *dx,
+ double *dy)
+{
+ SynapticsParameters *para = &priv->synpara;
+ double dtime = (hw->millis - HIST(0).millis) / 1000.0;
+
+ *dx = (hw->x - priv->trackstick_neutral_x);
+ *dy = (hw->y - priv->trackstick_neutral_y);
+
+ *dx = *dx * dtime * para->trackstick_speed;
+ *dy = *dy * dtime * para->trackstick_speed;
+}
+
+static void
+get_edge_speed(SynapticsPrivate * priv, const struct SynapticsHwState *hw,
+ edge_type edge, int *x_edge_speed, int *y_edge_speed)
+{
+ SynapticsParameters *para = &priv->synpara;
+
+ int minZ = para->edge_motion_min_z;
+ int maxZ = para->edge_motion_max_z;
+ int minSpd = para->edge_motion_min_speed;
+ int maxSpd = para->edge_motion_max_speed;
+ int edge_speed;
+
+ if (hw->z <= minZ) {
+ edge_speed = minSpd;
+ }
+ else if (hw->z >= maxZ) {
+ edge_speed = maxSpd;
+ }
+ else {
+ edge_speed =
+ minSpd + (hw->z - minZ) * (maxSpd - minSpd) / (maxZ - minZ);
+ }
+ if (!priv->synpara.circular_pad) {
+ /* on rectangular pad */
+ if (edge & RIGHT_EDGE) {
+ *x_edge_speed = edge_speed;
+ }
+ else if (edge & LEFT_EDGE) {
+ *x_edge_speed = -edge_speed;
+ }
+ if (edge & TOP_EDGE) {
+ *y_edge_speed = -edge_speed;
+ }
+ else if (edge & BOTTOM_EDGE) {
+ *y_edge_speed = edge_speed;
+ }
+ }
+ else if (edge) {
+ /* at edge of circular pad */
+ double relX, relY;
+
+ relative_coords(priv, hw->x, hw->y, &relX, &relY);
+ *x_edge_speed = (int) (edge_speed * relX);
+ *y_edge_speed = (int) (edge_speed * relY);
+ }
+}
+
+static void
+get_delta(SynapticsPrivate * priv, const struct SynapticsHwState *hw,
+ edge_type edge, double *dx, double *dy)
+{
+ SynapticsParameters *para = &priv->synpara;
+ double dtime = (hw->millis - HIST(0).millis) / 1000.0;
+ double integral;
+ double tmpf;
+ int x_edge_speed = 0;
+ int y_edge_speed = 0;
+
+ *dx = hw->x - HIST(0).x;
+ *dy = hw->y - HIST(0).y;
+
+ if ((priv->tap_state == TS_DRAG) || para->edge_motion_use_always)
+ get_edge_speed(priv, hw, edge, &x_edge_speed, &y_edge_speed);
+
+ /* report edge speed as synthetic motion. Of course, it would be
+ * cooler to report floats than to buffer, but anyway. */
+
+ /* FIXME: When these values go NaN, bad things happen. Root cause is unknown
+ * thus far though. */
+ if (isnan(priv->frac_x))
+ priv->frac_x = 0;
+ if (isnan(priv->frac_y))
+ priv->frac_y = 0;
+
+ tmpf = *dx + x_edge_speed * dtime + priv->frac_x;
+ priv->frac_x = modf(tmpf, &integral);
+ *dx = integral;
+ tmpf = *dy + y_edge_speed * dtime + priv->frac_y;
+ priv->frac_y = modf(tmpf, &integral);
+ *dy = integral;
+}
+
+/**
+ * Compute relative motion ('deltas') including edge motion xor trackstick.
+ */
+static int
+ComputeDeltas(SynapticsPrivate * priv, const struct SynapticsHwState *hw,
+ edge_type edge, int *dxP, int *dyP, Bool inside_area)
+{
+ enum MovingState moving_state;
+ double dx, dy;
+ int delay = 1000000000;
+
+ dx = dy = 0;
+
+ moving_state = priv->moving_state;
+ if (moving_state == MS_FALSE) {
+ switch (priv->tap_state) {
+ case TS_MOVE:
+ case TS_DRAG:
+ moving_state = MS_TOUCHPAD_RELATIVE;
+ break;
+ case TS_1:
+ case TS_3:
+ case TS_5:
+ moving_state = MS_TOUCHPAD_RELATIVE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!inside_area || !moving_state || priv->finger_state == FS_BLOCKED ||
+ priv->vert_scroll_edge_on || priv->horiz_scroll_edge_on ||
+ priv->vert_scroll_twofinger_on || priv->horiz_scroll_twofinger_on ||
+ priv->circ_scroll_on || priv->prevFingers != hw->numFingers ||
+ (moving_state == MS_TOUCHPAD_RELATIVE && hw->numFingers != 1)) {
+ /* reset packet counter. */
+ priv->count_packet_finger = 0;
+ goto out;
+ }
+
+ /* To create the illusion of fluid motion, call back at roughly the report
+ * rate, even in the absence of new hardware events; see comment above
+ * POLL_MS declaration. */
+ delay = MIN(delay, POLL_MS);
+
+ if (priv->count_packet_finger <= 1)
+ goto out; /* skip the lot */
+
+ if (priv->moving_state == MS_TRACKSTICK)
+ get_delta_for_trackstick(priv, hw, &dx, &dy);
+ else if (moving_state == MS_TOUCHPAD_RELATIVE)
+ get_delta(priv, hw, edge, &dx, &dy);
+
+ out:
+ priv->prevFingers = hw->numFingers;
+
+ *dxP = dx;
+ *dyP = dy;
+
+ return delay;
+}
+
+static double
+estimate_delta_circ(SynapticsPrivate * priv)
+{
+ double a1 = angle(priv, HIST(3).x, HIST(3).y);
+ double a2 = angle(priv, HIST(2).x, HIST(2).y);
+ double a3 = angle(priv, HIST(1).x, HIST(1).y);
+ double a4 = angle(priv, HIST(0).x, HIST(0).y);
+ double d1 = diffa(a2, a1);
+ double d2 = d1 + diffa(a3, a2);
+ double d3 = d2 + diffa(a4, a3);
+
+ return estimate_delta(d3, d2, d1, 0);
+}
+
+/* vert and horiz are to know which direction to start coasting
+ * circ is true if the user had been circular scrolling.
+ */
+static void
+start_coasting(SynapticsPrivate * priv, struct SynapticsHwState *hw,
+ Bool vert, Bool horiz, Bool circ)
+{
+ SynapticsParameters *para = &priv->synpara;
+
+ priv->scroll.coast_delta_y = 0.0;
+ priv->scroll.coast_delta_x = 0.0;
+
+ if ((priv->scroll.packets_this_scroll > 3) && (para->coasting_speed > 0.0)) {
+ double pkt_time = HIST_DELTA(0, 3, millis) / 1000.0;
+
+ if (vert && !circ) {
+ double dy =
+ estimate_delta(HIST(0).y, HIST(1).y, HIST(2).y, HIST(3).y);
+ if (pkt_time > 0) {
+ double scrolls_per_sec = (dy / abs(para->scroll_dist_vert)) / pkt_time;
+
+ if (fabs(scrolls_per_sec) >= para->coasting_speed) {
+ priv->scroll.coast_speed_y = scrolls_per_sec;
+ priv->scroll.coast_delta_y = (hw->y - priv->scroll.last_y);
+ }
+ }
+ }
+ if (horiz && !circ) {
+ double dx =
+ estimate_delta(HIST(0).x, HIST(1).x, HIST(2).x, HIST(3).x);
+ if (pkt_time > 0) {
+ double scrolls_per_sec = (dx / abs(para->scroll_dist_vert)) / pkt_time;
+
+ if (fabs(scrolls_per_sec) >= para->coasting_speed) {
+ priv->scroll.coast_speed_x = scrolls_per_sec;
+ priv->scroll.coast_delta_x = (hw->x - priv->scroll.last_x);
+ }
+ }
+ }
+ if (circ) {
+ double da = estimate_delta_circ(priv);
+
+ if (pkt_time > 0) {
+ double scrolls_per_sec = (da / para->scroll_dist_circ) / pkt_time;
+
+ if (fabs(scrolls_per_sec) >= para->coasting_speed) {
+ if (vert) {
+ priv->scroll.coast_speed_y = scrolls_per_sec;
+ priv->scroll.coast_delta_y =
+ diffa(priv->scroll.last_a,
+ angle(priv, hw->x, hw->y));
+ }
+ else if (horiz) {
+ priv->scroll.coast_speed_x = scrolls_per_sec;
+ priv->scroll.coast_delta_x =
+ diffa(priv->scroll.last_a,
+ angle(priv, hw->x, hw->y));
+ }
+ }
+ }
+ }
+ }
+ priv->scroll.packets_this_scroll = 0;
+}
+
+static void
+stop_coasting(SynapticsPrivate * priv)
+{
+ priv->scroll.coast_speed_x = 0;
+ priv->scroll.coast_speed_y = 0;
+ priv->scroll.packets_this_scroll = 0;
+}
+
+static int
+HandleScrolling(SynapticsPrivate * priv, struct SynapticsHwState *hw,
+ edge_type edge, Bool finger)
+{
+ SynapticsParameters *para = &priv->synpara;
+ int delay = 1000000000;
+
+ if ((priv->synpara.touchpad_off == 2) || (priv->finger_state == FS_BLOCKED)) {
+ stop_coasting(priv);
+ priv->circ_scroll_on = FALSE;
+ priv->vert_scroll_edge_on = FALSE;
+ priv->horiz_scroll_edge_on = FALSE;
+ priv->vert_scroll_twofinger_on = FALSE;
+ priv->horiz_scroll_twofinger_on = FALSE;
+ return delay;
+ }
+
+ /* scroll detection */
+ if (finger && priv->finger_state == FS_UNTOUCHED) {
+ stop_coasting(priv);
+ priv->scroll.delta_y = 0;
+ priv->scroll.delta_x = 0;
+ if (para->circular_scrolling) {
+ if ((para->circular_trigger == 0 && edge) ||
+ (para->circular_trigger == 1 && edge & TOP_EDGE) ||
+ (para->circular_trigger == 2 && edge & TOP_EDGE &&
+ edge & RIGHT_EDGE) || (para->circular_trigger == 3 &&
+ edge & RIGHT_EDGE) ||
+ (para->circular_trigger == 4 && edge & RIGHT_EDGE &&
+ edge & BOTTOM_EDGE) || (para->circular_trigger == 5 &&
+ edge & BOTTOM_EDGE) ||
+ (para->circular_trigger == 6 && edge & BOTTOM_EDGE &&
+ edge & LEFT_EDGE) || (para->circular_trigger == 7 &&
+ edge & LEFT_EDGE) ||
+ (para->circular_trigger == 8 && edge & LEFT_EDGE &&
+ edge & TOP_EDGE)) {
+ priv->circ_scroll_on = TRUE;
+ priv->circ_scroll_vert = TRUE;
+ priv->scroll.last_a = angle(priv, hw->x, hw->y);
+ DBG(7, "circular scroll detected on edge\n");
+ }
+ }
+ }
+ if (!priv->circ_scroll_on) {
+ if (finger) {
+ if (hw->numFingers == 2) {
+ if (!priv->vert_scroll_twofinger_on &&
+ (para->scroll_twofinger_vert) &&
+ (para->scroll_dist_vert != 0)) {
+ stop_coasting(priv);
+ priv->vert_scroll_twofinger_on = TRUE;
+ priv->vert_scroll_edge_on = FALSE;
+ priv->scroll.last_y = hw->y;
+ DBG(7, "vert two-finger scroll detected\n");
+ }
+ if (!priv->horiz_scroll_twofinger_on &&
+ (para->scroll_twofinger_horiz) &&
+ (para->scroll_dist_horiz != 0)) {
+ stop_coasting(priv);
+ priv->horiz_scroll_twofinger_on = TRUE;
+ priv->horiz_scroll_edge_on = FALSE;
+ priv->scroll.last_x = hw->x;
+ DBG(7, "horiz two-finger scroll detected\n");
+ }
+ }
+ }
+ if (finger && priv->finger_state == FS_UNTOUCHED) {
+ if (!priv->vert_scroll_twofinger_on &&
+ !priv->horiz_scroll_twofinger_on) {
+ if ((para->scroll_edge_vert) && (para->scroll_dist_vert != 0) &&
+ (edge & RIGHT_EDGE)) {
+ priv->vert_scroll_edge_on = TRUE;
+ priv->scroll.last_y = hw->y;
+ DBG(7, "vert edge scroll detected on right edge\n");
+ }
+ if ((para->scroll_edge_horiz) && (para->scroll_dist_horiz != 0)
+ && (edge & BOTTOM_EDGE)) {
+ priv->horiz_scroll_edge_on = TRUE;
+ priv->scroll.last_x = hw->x;
+ DBG(7, "horiz edge scroll detected on bottom edge\n");
+ }
+ }
+ }
+ }
+ {
+ Bool oldv = priv->vert_scroll_twofinger_on || priv->vert_scroll_edge_on
+ || (priv->circ_scroll_on && priv->circ_scroll_vert);
+
+ Bool oldh = priv->horiz_scroll_twofinger_on ||
+ priv->horiz_scroll_edge_on || (priv->circ_scroll_on &&
+ !priv->circ_scroll_vert);
+
+ Bool oldc = priv->circ_scroll_on;
+
+ if (priv->circ_scroll_on && !finger) {
+ /* circular scroll locks in until finger is raised */
+ DBG(7, "cicular scroll off\n");
+ priv->circ_scroll_on = FALSE;
+ }
+
+ if (!finger || hw->numFingers != 2) {
+ if (priv->vert_scroll_twofinger_on) {
+ DBG(7, "vert two-finger scroll off\n");
+ priv->vert_scroll_twofinger_on = FALSE;
+ }
+ if (priv->horiz_scroll_twofinger_on) {
+ DBG(7, "horiz two-finger scroll off\n");
+ priv->horiz_scroll_twofinger_on = FALSE;
+ }
+ }
+
+ if (priv->vert_scroll_edge_on && (!(edge & RIGHT_EDGE) || !finger)) {
+ DBG(7, "vert edge scroll off\n");
+ priv->vert_scroll_edge_on = FALSE;
+ }
+ if (priv->horiz_scroll_edge_on && (!(edge & BOTTOM_EDGE) || !finger)) {
+ DBG(7, "horiz edge scroll off\n");
+ priv->horiz_scroll_edge_on = FALSE;
+ }
+ /* If we were corner edge scrolling (coasting),
+ * but no longer in corner or raised a finger, then stop coasting. */
+ if (para->scroll_edge_corner &&
+ (priv->scroll.coast_speed_x || priv->scroll.coast_speed_y)) {
+ Bool is_in_corner = ((edge & RIGHT_EDGE) &&
+ (edge & (TOP_EDGE | BOTTOM_EDGE))) ||
+ ((edge & BOTTOM_EDGE) && (edge & (LEFT_EDGE | RIGHT_EDGE)));
+ if (!is_in_corner || !finger) {
+ DBG(7, "corner edge scroll off\n");
+ stop_coasting(priv);
+ }
+ }
+ /* if we were scrolling, but couldn't corner edge scroll,
+ * and are no longer scrolling, then start coasting */
+ oldv = oldv && !(priv->vert_scroll_twofinger_on ||
+ priv->vert_scroll_edge_on || (priv->circ_scroll_on &&
+ priv->circ_scroll_vert));
+
+ oldh = oldh && !(priv->horiz_scroll_twofinger_on ||
+ priv->horiz_scroll_edge_on || (priv->circ_scroll_on &&
+ !priv->
+ circ_scroll_vert));
+
+ oldc = oldc && !priv->circ_scroll_on;
+
+ if ((oldv || oldh) && !para->scroll_edge_corner) {
+ start_coasting(priv, hw, oldv, oldh, oldc);
+ }
+ }
+
+ /* if hitting a corner (top right or bottom right) while vertical
+ * scrolling is active, consider starting corner edge scrolling or
+ * switching over to circular scrolling smoothly */
+ if (priv->vert_scroll_edge_on && !priv->horiz_scroll_edge_on &&
+ (edge & RIGHT_EDGE) && (edge & (TOP_EDGE | BOTTOM_EDGE))) {
+ if (para->scroll_edge_corner) {
+ if (priv->scroll.coast_speed_y == 0) {
+ /* FYI: We can generate multiple start_coasting requests if
+ * we're in the corner, but we were moving so slowly when we
+ * got here that we didn't actually start coasting. */
+ DBG(7, "corner edge scroll on\n");
+ start_coasting(priv, hw, TRUE, FALSE, FALSE);
+ }
+ }
+ else if (para->circular_scrolling) {
+ priv->vert_scroll_edge_on = FALSE;
+ priv->circ_scroll_on = TRUE;
+ priv->circ_scroll_vert = TRUE;
+ priv->scroll.last_a = angle(priv, hw->x, hw->y);
+ DBG(7, "switching to circular scrolling\n");
+ }
+ }
+ /* Same treatment for horizontal scrolling */
+ if (priv->horiz_scroll_edge_on && !priv->vert_scroll_edge_on &&
+ (edge & BOTTOM_EDGE) && (edge & (LEFT_EDGE | RIGHT_EDGE))) {
+ if (para->scroll_edge_corner) {
+ if (priv->scroll.coast_speed_x == 0) {
+ /* FYI: We can generate multiple start_coasting requests if
+ * we're in the corner, but we were moving so slowly when we
+ * got here that we didn't actually start coasting. */
+ DBG(7, "corner edge scroll on\n");
+ start_coasting(priv, hw, FALSE, TRUE, FALSE);
+ }
+ }
+ else if (para->circular_scrolling) {
+ priv->horiz_scroll_edge_on = FALSE;
+ priv->circ_scroll_on = TRUE;
+ priv->circ_scroll_vert = FALSE;
+ priv->scroll.last_a = angle(priv, hw->x, hw->y);
+ DBG(7, "switching to circular scrolling\n");
+ }
+ }
+
+ if (priv->vert_scroll_edge_on || priv->horiz_scroll_edge_on ||
+ priv->vert_scroll_twofinger_on || priv->horiz_scroll_twofinger_on ||
+ priv->circ_scroll_on) {
+ priv->scroll.packets_this_scroll++;
+ }
+
+ if (priv->vert_scroll_edge_on || priv->vert_scroll_twofinger_on) {
+ /* + = down, - = up */
+ if (para->scroll_dist_vert != 0 && hw->y != priv->scroll.last_y) {
+ priv->scroll.delta_y += (hw->y - priv->scroll.last_y);
+ priv->scroll.last_y = hw->y;
+ }
+ }
+ if (priv->horiz_scroll_edge_on || priv->horiz_scroll_twofinger_on) {
+ /* + = right, - = left */
+ if (para->scroll_dist_horiz != 0 && hw->x != priv->scroll.last_x) {
+ priv->scroll.delta_x += (hw->x - priv->scroll.last_x);
+ priv->scroll.last_x = hw->x;
+ }
+ }
+ if (priv->circ_scroll_on) {
+ /* + = counter clockwise, - = clockwise */
+ double delta = para->scroll_dist_circ;
+ double diff = diffa(priv->scroll.last_a, angle(priv, hw->x, hw->y));
+
+ if (delta >= 0.005 && diff != 0.0) {
+ if (priv->circ_scroll_vert)
+ priv->scroll.delta_y -= diff / delta * para->scroll_dist_vert;
+ else
+ priv->scroll.delta_x -= diff / delta * para->scroll_dist_horiz;
+ priv->scroll.last_a = angle(priv, hw->x, hw->y);
+ }
+ }
+
+ if (priv->scroll.coast_speed_y) {
+ double dtime = (hw->millis - priv->scroll.last_millis) / 1000.0;
+ double ddy = para->coasting_friction * dtime;
+
+ priv->scroll.delta_y += priv->scroll.coast_speed_y * dtime * abs(para->scroll_dist_vert);
+ delay = MIN(delay, POLL_MS);
+ if (abs(priv->scroll.coast_speed_y) < ddy) {
+ priv->scroll.coast_speed_y = 0;
+ priv->scroll.packets_this_scroll = 0;
+ }
+ else {
+ priv->scroll.coast_speed_y +=
+ (priv->scroll.coast_speed_y < 0 ? ddy : -ddy);
+ }
+ }
+
+ if (priv->scroll.coast_speed_x) {
+ double dtime = (hw->millis - priv->scroll.last_millis) / 1000.0;
+ double ddx = para->coasting_friction * dtime;
+ priv->scroll.delta_x += priv->scroll.coast_speed_x * dtime * abs(para->scroll_dist_horiz);
+ delay = MIN(delay, POLL_MS);
+ if (abs(priv->scroll.coast_speed_x) < ddx) {
+ priv->scroll.coast_speed_x = 0;
+ priv->scroll.packets_this_scroll = 0;
+ }
+ else {
+ priv->scroll.coast_speed_x +=
+ (priv->scroll.coast_speed_x < 0 ? ddx : -ddx);
+ }
+ }
+
+ return delay;
+}
+
+/**
+ * Check if any 2+ fingers are close enough together to assume this is a
+ * ClickFinger action.
+ */
+static int
+clickpad_guess_clickfingers(SynapticsPrivate * priv,
+ struct SynapticsHwState *hw)
+{
+ int nfingers = 0;
+
+#if HAVE_MULTITOUCH
+ char close_point[SYNAPTICS_MAX_TOUCHES] = { 0 }; /* 1 for each point close
+ to another one */
+ int i, j;
+
+ for (i = 0; i < hw->num_mt_mask - 1; i++) {
+ ValuatorMask *f1;
+
+ if (hw->slot_state[i] == SLOTSTATE_EMPTY ||
+ hw->slot_state[i] == SLOTSTATE_CLOSE)
+ continue;
+
+ f1 = hw->mt_mask[i];
+
+ for (j = i + 1; j < hw->num_mt_mask; j++) {
+ ValuatorMask *f2;
+ double x1, x2, y1, y2;
+
+ if (hw->slot_state[j] == SLOTSTATE_EMPTY ||
+ hw->slot_state[j] == SLOTSTATE_CLOSE)
+ continue;
+
+ f2 = hw->mt_mask[j];
+
+ x1 = valuator_mask_get_double(f1, 0);
+ y1 = valuator_mask_get_double(f1, 1);
+
+ x2 = valuator_mask_get_double(f2, 0);
+ y2 = valuator_mask_get_double(f2, 1);
+
+ /* FIXME: fingers closer together than 30% of touchpad width, but
+ * really, this should be dependent on the touchpad size. Also,
+ * you'll need to find a touchpad that doesn't lie about it's
+ * size. Good luck. */
+ if (abs(x1 - x2) < (priv->maxx - priv->minx) * .3 &&
+ abs(y1 - y2) < (priv->maxy - priv->miny) * .3) {
+ close_point[j] = 1;
+ close_point[i] = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < SYNAPTICS_MAX_TOUCHES; i++)
+ nfingers += close_point[i];
+#endif
+
+ return nfingers;
+}
+
+static void
+handle_clickfinger(SynapticsPrivate * priv, struct SynapticsHwState *hw)
+{
+ SynapticsParameters *para = &priv->synpara;
+ int action = 0;
+ int nfingers = hw->numFingers;
+
+ /* if this is a clickpad, clickfinger handling is:
+ * one finger down: no action, this is a normal click
+ * two fingers down: F2_CLICK
+ * three fingers down: F3_CLICK
+ */
+
+ if (para->clickpad)
+ nfingers = clickpad_guess_clickfingers(priv, hw);
+
+ switch (nfingers) {
+ case 1:
+ action = para->click_action[F1_CLICK1];
+ break;
+ case 2:
+ action = para->click_action[F2_CLICK1];
+ break;
+ case 3:
+ action = para->click_action[F3_CLICK1];
+ break;
+ }
+ switch (action) {
+ case 1:
+ hw->left = 1 | BTN_EMULATED_FLAG;
+ break;
+ case 2:
+ hw->left = 0;
+ hw->middle = 1 | BTN_EMULATED_FLAG;
+ break;
+ case 3:
+ hw->left = 0;
+ hw->right = 1 | BTN_EMULATED_FLAG;
+ break;
+ }
+}
+
+/* Update the hardware state in shared memory. This is read-only these days,
+ * nothing in the driver reads back from SHM. SHM configuration is a thing of the past.
+ */
+static void
+update_shm(const InputInfoPtr pInfo, const struct SynapticsHwState *hw)
+{
+ int i;
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+ SynapticsSHM *shm = priv->synshm;
+
+ if (!shm)
+ return;
+
+ shm->x = hw->x;
+ shm->y = hw->y;
+ shm->z = hw->z;
+ shm->numFingers = hw->numFingers;
+ shm->fingerWidth = hw->fingerWidth;
+ shm->left = hw->left;
+ shm->right = hw->right;
+ shm->up = hw->up;
+ shm->down = hw->down;
+ for (i = 0; i < 8; i++)
+ shm->multi[i] = hw->multi[i];
+ shm->middle = hw->middle;
+}
+
+/* Adjust the hardware state according to the extra buttons (if the touchpad
+ * has any and not many touchpads do these days). These buttons are up/down
+ * tilt buttons and/or left/right buttons that then map into a specific
+ * function (or scrolling into).
+ */
+static Bool
+adjust_state_from_scrollbuttons(const InputInfoPtr pInfo,
+ struct SynapticsHwState *hw)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+ SynapticsParameters *para = &priv->synpara;
+ Bool double_click = FALSE;
+
+ if (!para->updown_button_scrolling) {
+ if (hw->down) { /* map down button to middle button */
+ hw->middle = TRUE;
+ }
+
+ if (hw->up) { /* up button generates double click */
+ if (!priv->prev_up)
+ double_click = TRUE;
+ }
+ priv->prev_up = hw->up;
+
+ /* reset up/down button events */
+ hw->up = hw->down = FALSE;
+ }
+
+ /* Left/right button scrolling, or middle clicks */
+ if (!para->leftright_button_scrolling) {
+ if (hw->multi[2] || hw->multi[3])
+ hw->middle = TRUE;
+
+ /* reset left/right button events */
+ hw->multi[2] = hw->multi[3] = FALSE;
+ }
+
+ return double_click;
+}
+
+static void
+update_hw_button_state(const InputInfoPtr pInfo, struct SynapticsHwState *hw,
+ struct SynapticsHwState *old, CARD32 now, int *delay)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+ SynapticsParameters *para = &priv->synpara;
+
+ /* Treat the first two multi buttons as up/down for now. */
+ hw->up |= hw->multi[0];
+ hw->down |= hw->multi[1];
+
+ /* 3rd button emulation */
+ hw->middle |= HandleMidButtonEmulation(priv, hw, now, delay);
+
+ /* If this is a clickpad and the user clicks in a soft button area, press
+ * the soft button instead. */
+ if (para->clickpad) {
+ /* hw->left is down, but no other buttons were already down */
+ if (!old->left && !old->right && !old->middle &&
+ hw->left && !hw->right && !hw->middle) {
+ if (is_inside_rightbutton_area(para, hw->x, hw->y)) {
+ hw->left = 0;
+ hw->right = 1;
+ }
+ else if (is_inside_middlebutton_area(para, hw->x, hw->y)) {
+ hw->left = 0;
+ hw->middle = 1;
+ }
+ }
+ else if (hw->left) {
+ hw->left = old->left;
+ hw->right = old->right;
+ hw->middle = old->middle;
+ }
+ }
+
+ /* Fingers emulate other buttons. ClickFinger can only be
+ triggered on transition, when left is pressed
+ */
+ if (hw->left && !old->left && !old->middle && !old->right &&
+ hw->numFingers >= 1) {
+ handle_clickfinger(priv, hw);
+ }
+
+ /* Two finger emulation */
+ if (hw->numFingers == 1 && hw->z >= para->emulate_twofinger_z &&
+ hw->fingerWidth >= para->emulate_twofinger_w) {
+ hw->numFingers = 2;
+ }
+}
+
+static void
+post_button_click(const InputInfoPtr pInfo, const int button)
+{
+ xf86PostButtonEvent(pInfo->dev, FALSE, button, TRUE, 0, 0);
+ xf86PostButtonEvent(pInfo->dev, FALSE, button, FALSE, 0, 0);
+}
+
+static void
+post_scroll_events(const InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+
+#ifdef HAVE_SMOOTH_SCROLL
+ valuator_mask_zero(priv->scroll_events_mask);
+
+ if (priv->scroll.delta_y != 0.0) {
+ valuator_mask_set_double(priv->scroll_events_mask,
+ priv->scroll_axis_vert, priv->scroll.delta_y);
+ priv->scroll.delta_y = 0;
+ }
+ if (priv->scroll.delta_x != 0.0) {
+ valuator_mask_set_double(priv->scroll_events_mask,
+ priv->scroll_axis_horiz, priv->scroll.delta_x);
+ priv->scroll.delta_x = 0;
+ }
+ if (valuator_mask_num_valuators(priv->scroll_events_mask))
+ xf86PostMotionEventM(pInfo->dev, FALSE, priv->scroll_events_mask);
+#else
+ SynapticsParameters *para = &priv->synpara;
+
+ /* smooth scrolling uses the dist as increment */
+
+ while (priv->scroll.delta_y <= -para->scroll_dist_vert) {
+ post_button_click(pInfo, 4);
+ priv->scroll.delta_y += para->scroll_dist_vert;
+ }
+
+ while (priv->scroll.delta_y >= para->scroll_dist_vert) {
+ post_button_click(pInfo, 5);
+ priv->scroll.delta_y -= para->scroll_dist_vert;
+ }
+
+ while (priv->scroll.delta_x <= -para->scroll_dist_horiz) {
+ post_button_click(pInfo, 6);
+ priv->scroll.delta_x += para->scroll_dist_horiz;
+ }
+
+ while (priv->scroll.delta_x >= para->scroll_dist_horiz) {
+ post_button_click(pInfo, 7);
+ priv->scroll.delta_x -= para->scroll_dist_horiz;
+ }
+#endif
+}
+
+static inline int
+repeat_scrollbuttons(const InputInfoPtr pInfo,
+ const struct SynapticsHwState *hw,
+ int buttons, CARD32 now, int delay)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+ SynapticsParameters *para = &priv->synpara;
+ int repeat_delay, timeleft;
+ int rep_buttons = 0;
+
+ if (para->updown_button_repeat)
+ rep_buttons |= (1 << (4 - 1)) | (1 << (5 - 1));
+ if (para->leftright_button_repeat)
+ rep_buttons |= (1 << (6 - 1)) | (1 << (7 - 1));
+
+ /* Handle auto repeat buttons */
+ repeat_delay = clamp(para->scroll_button_repeat, SBR_MIN, SBR_MAX);
+ if (((hw->up || hw->down) && para->updown_button_repeat &&
+ para->updown_button_scrolling) ||
+ ((hw->multi[2] || hw->multi[3]) && para->leftright_button_repeat &&
+ para->leftright_button_scrolling)) {
+ priv->repeatButtons = buttons & rep_buttons;
+ if (!priv->nextRepeat) {
+ priv->nextRepeat = now + repeat_delay * 2;
+ }
+ }
+ else {
+ priv->repeatButtons = 0;
+ priv->nextRepeat = 0;
+ }
+
+ if (priv->repeatButtons) {
+ timeleft = TIME_DIFF(priv->nextRepeat, now);
+ if (timeleft > 0)
+ delay = MIN(delay, timeleft);
+ if (timeleft <= 0) {
+ int change, id;
+
+ change = priv->repeatButtons;
+ while (change) {
+ id = ffs(change);
+ change &= ~(1 << (id - 1));
+ if (id == 4)
+ priv->scroll.delta_y -= para->scroll_dist_vert;
+ else if (id == 5)
+ priv->scroll.delta_y += para->scroll_dist_vert;
+ else if (id == 6)
+ priv->scroll.delta_x -= para->scroll_dist_horiz;
+ else if (id == 7)
+ priv->scroll.delta_x += para->scroll_dist_horiz;
+ }
+
+ priv->nextRepeat = now + repeat_delay;
+ delay = MIN(delay, repeat_delay);
+ }
+ }
+
+ return delay;
+}
+
+/* Update the open slots and number of active touches */
+static void
+UpdateTouchState(InputInfoPtr pInfo, struct SynapticsHwState *hw)
+{
+#ifdef HAVE_MULTITOUCH
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ int i;
+
+ for (i = 0; i < hw->num_mt_mask; i++) {
+ if (hw->slot_state[i] == SLOTSTATE_OPEN) {
+ priv->open_slots[priv->num_active_touches] = i;
+ priv->num_active_touches++;
+ }
+ else if (hw->slot_state[i] == SLOTSTATE_CLOSE) {
+ Bool found = FALSE;
+ int j;
+
+ for (j = 0; j < priv->num_active_touches - 1; j++) {
+ if (priv->open_slots[j] == i)
+ found = TRUE;
+
+ if (found)
+ priv->open_slots[j] = priv->open_slots[j + 1];
+ }
+
+ BUG_WARN(priv->num_active_touches == 0);
+ if (priv->num_active_touches > 0)
+ priv->num_active_touches--;
+ }
+ }
+
+ SynapticsResetTouchHwState(hw, FALSE);
+#endif
+}
+
+static void
+HandleTouches(InputInfoPtr pInfo, struct SynapticsHwState *hw)
+{
+#ifdef HAVE_MULTITOUCH
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ SynapticsParameters *para = &priv->synpara;
+ int new_active_touches = priv->num_active_touches;
+ int min_touches = 2;
+ Bool restart_touches = FALSE;
+ int i;
+
+ if (para->click_action[F3_CLICK1] || para->tap_action[F3_TAP])
+ min_touches = 4;
+ else if (para->click_action[F2_CLICK1] || para->tap_action[F2_TAP] ||
+ para->scroll_twofinger_vert || para->scroll_twofinger_horiz)
+ min_touches = 3;
+
+ /* Count new number of active touches */
+ for (i = 0; i < hw->num_mt_mask; i++) {
+ if (hw->slot_state[i] == SLOTSTATE_OPEN)
+ new_active_touches++;
+ else if (hw->slot_state[i] == SLOTSTATE_CLOSE)
+ new_active_touches--;
+ }
+
+ if (priv->has_semi_mt)
+ goto out;
+
+ if (priv->num_active_touches < min_touches &&
+ new_active_touches < min_touches) {
+ /* We stayed below number of touches needed to send events */
+ goto out;
+ }
+ else if (priv->num_active_touches >= min_touches &&
+ new_active_touches < min_touches) {
+ /* We are transitioning to less than the number of touches needed to
+ * send events. End all currently open touches. */
+ for (i = 0; i < priv->num_active_touches; i++) {
+ int slot = priv->open_slots[i];
+
+ xf86PostTouchEvent(pInfo->dev, slot, XI_TouchEnd, 0,
+ hw->mt_mask[slot]);
+ }
+
+ /* Don't send any more events */
+ goto out;
+ }
+ else if (priv->num_active_touches < min_touches &&
+ new_active_touches >= min_touches) {
+ /* We are transitioning to more than the number of touches needed to
+ * send events. Begin all already open touches. */
+ restart_touches = TRUE;
+ for (i = 0; i < priv->num_active_touches; i++) {
+ int slot = priv->open_slots[i];
+
+ xf86PostTouchEvent(pInfo->dev, slot, XI_TouchBegin, 0,
+ hw->mt_mask[slot]);
+ }
+ }
+
+ /* Send touch begin events for all new touches */
+ for (i = 0; i < hw->num_mt_mask; i++)
+ if (hw->slot_state[i] == SLOTSTATE_OPEN)
+ xf86PostTouchEvent(pInfo->dev, i, XI_TouchBegin, 0, hw->mt_mask[i]);
+
+ /* Send touch update/end events for all the rest */
+ for (i = 0; i < priv->num_active_touches; i++) {
+ int slot = priv->open_slots[i];
+
+ /* Don't send update event if we just reopened the touch above */
+ if (hw->slot_state[slot] == SLOTSTATE_UPDATE && !restart_touches)
+ xf86PostTouchEvent(pInfo->dev, slot, XI_TouchUpdate, 0,
+ hw->mt_mask[slot]);
+ else if (hw->slot_state[slot] == SLOTSTATE_CLOSE)
+ xf86PostTouchEvent(pInfo->dev, slot, XI_TouchEnd, 0,
+ hw->mt_mask[slot]);
+ }
+
+ out:
+ UpdateTouchState(pInfo, hw);
+#endif
+}
+
+static void
+filter_jitter(SynapticsPrivate * priv, int *x, int *y)
+{
+ SynapticsParameters *para = &priv->synpara;
+
+ priv->hyst_center_x = hysteresis(*x, priv->hyst_center_x, para->hyst_x);
+ priv->hyst_center_y = hysteresis(*y, priv->hyst_center_y, para->hyst_y);
+ *x = priv->hyst_center_x;
+ *y = priv->hyst_center_y;
+}
+
+static void
+reset_hw_state(struct SynapticsHwState *hw)
+{
+ hw->x = 0;
+ hw->y = 0;
+ hw->z = 0;
+ hw->numFingers = 0;
+ hw->fingerWidth = 0;
+}
+
+/*
+ * React on changes in the hardware state. This function is called every time
+ * the hardware state changes. The return value is used to specify how many
+ * milliseconds to wait before calling the function again if no state change
+ * occurs.
+ *
+ * from_timer denotes if HandleState was triggered from a timer (e.g. to
+ * generate fake motion events, or for the tap-to-click state machine), rather
+ * than from having received a motion event.
+ */
+static int
+HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw, CARD32 now,
+ Bool from_timer)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+ SynapticsParameters *para = &priv->synpara;
+ enum FingerState finger = FS_UNTOUCHED;
+ int dx = 0, dy = 0, buttons, id;
+ edge_type edge = NO_EDGE;
+ int change;
+ int double_click = FALSE;
+ int delay = 1000000000;
+ int timeleft;
+ Bool inside_active_area;
+
+ update_shm(pInfo, hw);
+
+ /* If touchpad is switched off, we skip the whole thing and return delay */
+ if (para->touchpad_off == 1) {
+ UpdateTouchState(pInfo, hw);
+ return delay;
+ }
+
+ /* We need both and x/y, the driver can't handle just one of the two
+ * yet. But since it's possible to hit a phys button on non-clickpads
+ * without ever getting motion data first, we must continue with 0/0 for
+ * that case. */
+ if (hw->x == INT_MIN || hw->y == INT_MAX) {
+ if (para->clickpad)
+ return delay;
+ else if (hw->left || hw->right || hw->middle) {
+ hw->x = (hw->x == INT_MIN) ? 0 : hw->x;
+ hw->y = (hw->y == INT_MIN) ? 0 : hw->y;
+ }
+ }
+
+ /* If a physical button is pressed on a clickpad, use cumulative relative
+ * touch movements for motion */
+ if (para->clickpad && (hw->left || hw->right || hw->middle)) {
+ hw->x = hw->cumulative_dx;
+ hw->y = hw->cumulative_dy;
+ }
+
+ /* apply hysteresis before doing anything serious. This cancels
+ * out a lot of noise which might surface in strange phenomena
+ * like flicker in scrolling or noise motion. */
+ filter_jitter(priv, &hw->x, &hw->y);
+
+ inside_active_area = is_inside_active_area(priv, hw->x, hw->y);
+
+ /* now we know that these _coordinates_ aren't in the area.
+ invalid are: x, y, z, numFingers, fingerWidth
+ valid are: millis, left/right/middle/up/down/etc.
+ */
+ if (!inside_active_area) {
+ reset_hw_state(hw);
+
+ /* FIXME: if finger accidentally moves into the area and doesn't
+ * really release, the finger should remain down. */
+ }
+
+ /* these two just update hw->left, right, etc. */
+ update_hw_button_state(pInfo, hw, priv->old_hw_state, now, &delay);
+ if (priv->has_scrollbuttons)
+ double_click = adjust_state_from_scrollbuttons(pInfo, hw);
+
+ /* no edge or finger detection outside of area */
+ if (inside_active_area) {
+ edge = edge_detection(priv, hw->x, hw->y);
+ if (!from_timer)
+ finger = SynapticsDetectFinger(priv, hw);
+ else
+ finger = priv->finger_state;
+ }
+
+ /* tap and drag detection. Needs to be performed even if the finger is in
+ * the dead area to reset the state. */
+ timeleft = HandleTapProcessing(priv, hw, now, finger, inside_active_area);
+ if (timeleft > 0)
+ delay = MIN(delay, timeleft);
+
+ if (inside_active_area) {
+ /* Don't bother about scrolling in the dead area of the touchpad. */
+ timeleft = HandleScrolling(priv, hw, edge, (finger >= FS_TOUCHED));
+ if (timeleft > 0)
+ delay = MIN(delay, timeleft);
+
+ /*
+ * Compensate for unequal x/y resolution. This needs to be done after
+ * calculations that require unadjusted coordinates, for example edge
+ * detection.
+ */
+ ScaleCoordinates(priv, hw);
+ }
+
+ dx = dy = 0;
+
+ if (!priv->absolute_events) {
+ timeleft = ComputeDeltas(priv, hw, edge, &dx, &dy, inside_active_area);
+ delay = MIN(delay, timeleft);
+ }
+
+ buttons = ((hw->left ? 0x01 : 0) |
+ (hw->middle ? 0x02 : 0) |
+ (hw->right ? 0x04 : 0) |
+ (hw->up ? 0x08 : 0) |
+ (hw->down ? 0x10 : 0) |
+ (hw->multi[2] ? 0x20 : 0) | (hw->multi[3] ? 0x40 : 0));
+
+ if (priv->tap_button > 0) {
+ int tap_mask = 1 << (priv->tap_button - 1);
+
+ if (priv->tap_button_state == TBS_BUTTON_DOWN_UP) {
+ if (tap_mask != (priv->lastButtons & tap_mask)) {
+ xf86PostButtonEvent(pInfo->dev, FALSE, priv->tap_button, TRUE,
+ 0, 0);
+ priv->lastButtons |= tap_mask;
+ }
+ priv->tap_button_state = TBS_BUTTON_UP;
+ }
+ if (priv->tap_button_state == TBS_BUTTON_DOWN)
+ buttons |= tap_mask;
+ }
+
+ /* Post events */
+ if (finger >= FS_TOUCHED) {
+ if (priv->absolute_events && inside_active_area) {
+ xf86PostMotionEvent(pInfo->dev, 1, 0, 2, hw->x, hw->y);
+ }
+ else if (dx || dy) {
+ xf86PostMotionEvent(pInfo->dev, 0, 0, 2, dx, dy);
+ }
+ }
+
+ if (priv->mid_emu_state == MBE_LEFT_CLICK) {
+ post_button_click(pInfo, 1);
+ priv->mid_emu_state = MBE_OFF;
+ }
+ else if (priv->mid_emu_state == MBE_RIGHT_CLICK) {
+ post_button_click(pInfo, 3);
+ priv->mid_emu_state = MBE_OFF;
+ }
+
+ change = buttons ^ priv->lastButtons;
+ while (change) {
+ id = ffs(change); /* number of first set bit 1..32 is returned */
+ change &= ~(1 << (id - 1));
+ xf86PostButtonEvent(pInfo->dev, FALSE, id, (buttons & (1 << (id - 1))),
+ 0, 0);
+ }
+
+ if (priv->has_scrollbuttons)
+ delay = repeat_scrollbuttons(pInfo, hw, buttons, now, delay);
+
+ /* Process scroll events only if coordinates are
+ * in the Synaptics Area
+ */
+ if (inside_active_area &&
+ (priv->scroll.delta_x != 0.0 || priv->scroll.delta_y != 0.0)) {
+ post_scroll_events(pInfo);
+ priv->scroll.last_millis = hw->millis;
+ }
+
+ if (double_click) {
+ post_button_click(pInfo, 1);
+ post_button_click(pInfo, 1);
+ }
+
+ HandleTouches(pInfo, hw);
+
+ /* Save old values of some state variables */
+ priv->finger_state = finger;
+ priv->lastButtons = buttons;
+
+ /* generate a history of the absolute positions */
+ if (inside_active_area)
+ store_history(priv, hw->x, hw->y, hw->millis);
+
+ /* Save logical state for transition comparisons */
+ SynapticsCopyHwState(priv->old_hw_state, hw);
+
+ return delay;
+}
+
+static int
+ControlProc(InputInfoPtr pInfo, xDeviceCtl * control)
+{
+ DBG(3, "Control Proc called\n");
+ return Success;
+}
+
+static int
+SwitchMode(ClientPtr client, DeviceIntPtr dev, int mode)
+{
+ InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
+ SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
+
+ DBG(3, "SwitchMode called\n");
+
+ switch (mode) {
+ case Absolute:
+ priv->absolute_events = TRUE;
+ break;
+
+ case Relative:
+ priv->absolute_events = FALSE;
+ break;
+
+ default:
+ return XI_BadMode;
+ }
+
+ return Success;
+}
+
+static void
+ReadDevDimensions(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+
+ if (priv->proto_ops->ReadDevDimensions)
+ priv->proto_ops->ReadDevDimensions(pInfo);
+
+ SanitizeDimensions(pInfo);
+}
+
+static Bool
+QueryHardware(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+
+ priv->comm.protoBufTail = 0;
+
+ if (!priv->proto_ops->QueryHardware(pInfo)) {
+ xf86IDrvMsg(pInfo, X_PROBED, "no supported touchpad found\n");
+ if (priv->proto_ops->DeviceOffHook)
+ priv->proto_ops->DeviceOffHook(pInfo);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+ScaleCoordinates(SynapticsPrivate * priv, struct SynapticsHwState *hw)
+{
+ int xCenter = (priv->synpara.left_edge + priv->synpara.right_edge) / 2;
+ int yCenter = (priv->synpara.top_edge + priv->synpara.bottom_edge) / 2;
+
+ hw->x = (hw->x - xCenter) * priv->horiz_coeff + xCenter;
+ hw->y = (hw->y - yCenter) * priv->vert_coeff + yCenter;
+}
+
+void
+CalculateScalingCoeffs(SynapticsPrivate * priv)
+{
+ int vertRes = priv->synpara.resolution_vert;
+ int horizRes = priv->synpara.resolution_horiz;
+
+ if ((horizRes > vertRes) && (horizRes > 0)) {
+ priv->horiz_coeff = vertRes / (double) horizRes;
+ priv->vert_coeff = 1;
+ }
+ else if ((horizRes < vertRes) && (vertRes > 0)) {
+ priv->horiz_coeff = 1;
+ priv->vert_coeff = horizRes / (double) vertRes;
+ }
+ else {
+ priv->horiz_coeff = 1;
+ priv->vert_coeff = 1;
+ }
+}
diff --git a/src/synapticsstr.h b/src/synapticsstr.h
new file mode 100644
index 0000000..dd6a09b
--- /dev/null
+++ b/src/synapticsstr.h
@@ -0,0 +1,292 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYNAPTICSSTR_H_
+#define _SYNAPTICSSTR_H_
+
+#include "synproto.h"
+#include <xserver-properties.h>
+
+#ifdef DBG
+#undef DBG
+#endif
+
+#ifdef DEBUG
+#define DBG(verb, ...) \
+ xf86MsgVerb(X_INFO, verb, __VA_ARGS__)
+#else
+#define DBG(verb, msg, ...) /* */
+#endif
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12
+#define xf86IDrvMsg(pInfo, type, ...) xf86Msg(type, __VA_ARGS__)
+#endif
+
+#ifdef AXIS_LABEL_PROP_REL_VSCROLL
+#define HAVE_SMOOTH_SCROLL
+#endif
+
+/******************************************************************************
+ * Definitions
+ * structs, typedefs, #defines, enums
+ *****************************************************************************/
+#define SYNAPTICS_MOVE_HISTORY 5
+#define SYNAPTICS_MAX_TOUCHES 10
+
+typedef struct _SynapticsMoveHist {
+ int x, y;
+ CARD32 millis;
+} SynapticsMoveHistRec;
+
+typedef struct _SynapticsTouchAxis {
+ const char *label;
+ int min;
+ int max;
+ int res;
+} SynapticsTouchAxisRec;
+
+enum FingerState { /* Note! The order matters. Compared with < operator. */
+ FS_BLOCKED = -1,
+ FS_UNTOUCHED = 0, /* this is 0 so it's the initialized value. */
+ FS_TOUCHED = 1,
+ FS_PRESSED = 2,
+};
+
+enum MovingState {
+ MS_FALSE,
+ MS_TOUCHPAD_RELATIVE,
+ MS_TRACKSTICK /* trackstick is always relative */
+};
+
+enum MidButtonEmulation {
+ MBE_OFF, /* No button pressed */
+ MBE_LEFT, /* Left button pressed, waiting for right button or timeout */
+ MBE_RIGHT, /* Right button pressed, waiting for left button or timeout */
+ MBE_MID, /* Left and right buttons pressed, waiting for both buttons
+ to be released */
+ MBE_TIMEOUT, /* Waiting for both buttons to be released. */
+ MBE_LEFT_CLICK, /* Emulate left button click. */
+ MBE_RIGHT_CLICK, /* Emulate right button click. */
+};
+
+/* See docs/tapndrag.dia for a state machine diagram */
+enum TapState {
+ TS_START, /* No tap/drag in progress */
+ TS_1, /* After first touch */
+ TS_MOVE, /* Pointer movement enabled */
+ TS_2A, /* After first release */
+ TS_2B, /* After second/third/... release */
+ TS_SINGLETAP, /* After timeout after first release */
+ TS_3, /* After second touch */
+ TS_DRAG, /* Pointer drag enabled */
+ TS_4, /* After release when "locked drags" enabled */
+ TS_5, /* After touch when "locked drags" enabled */
+ TS_CLICKPAD_MOVE, /* After left button press on a clickpad */
+};
+
+enum TapButtonState {
+ TBS_BUTTON_UP, /* "Virtual tap button" is up */
+ TBS_BUTTON_DOWN, /* "Virtual tap button" is down */
+ TBS_BUTTON_DOWN_UP /* Send button down event + set up state */
+};
+
+enum TouchpadModel {
+ MODEL_UNKNOWN = 0,
+ MODEL_SYNAPTICS,
+ MODEL_ALPS,
+ MODEL_APPLETOUCH,
+ MODEL_ELANTECH
+};
+
+typedef struct _SynapticsParameters {
+ /* Parameter data */
+ int left_edge, right_edge, top_edge, bottom_edge; /* edge coordinates absolute */
+ int finger_low, finger_high, finger_press; /* finger detection values in Z-values */
+ int tap_time;
+ int tap_move; /* max. tapping time and movement in packets and coord. */
+ int single_tap_timeout; /* timeout to recognize a single tap */
+ int tap_time_2; /* max. tapping time for double taps */
+ int click_time; /* The duration of a single click */
+ Bool clickpad; /* Device is a has integrated buttons */
+ Bool fast_taps; /* Faster reaction to single taps */
+ int emulate_mid_button_time; /* Max time between left and right button presses to
+ emulate a middle button press. */
+ int emulate_twofinger_z; /* pressure threshold to emulate two finger touch (for Alps) */
+ int emulate_twofinger_w; /* Finger width threshold to emulate two finger touch */
+ int scroll_dist_vert; /* Scrolling distance in absolute coordinates */
+ int scroll_dist_horiz; /* Scrolling distance in absolute coordinates */
+ Bool scroll_edge_vert; /* Enable/disable vertical scrolling on right edge */
+ Bool scroll_edge_horiz; /* Enable/disable horizontal scrolling on left edge */
+ Bool scroll_edge_corner; /* Enable/disable continuous edge scrolling when in the corner */
+ Bool scroll_twofinger_vert; /* Enable/disable vertical two-finger scrolling */
+ Bool scroll_twofinger_horiz; /* Enable/disable horizontal two-finger scrolling */
+ double min_speed, max_speed, accl; /* movement parameters */
+ double trackstick_speed; /* trackstick mode speed */
+ int edge_motion_min_z; /* finger pressure at which minimum edge motion speed is set */
+ int edge_motion_max_z; /* finger pressure at which maximum edge motion speed is set */
+ int edge_motion_min_speed; /* slowest setting for edge motion speed */
+ int edge_motion_max_speed; /* fastest setting for edge motion speed */
+ Bool edge_motion_use_always; /* If false, edge motion is used only when dragging */
+
+ Bool updown_button_scrolling; /* Up/Down-Button scrolling or middle/double-click */
+ Bool leftright_button_scrolling; /* Left/right-button scrolling, or two lots of middle button */
+ Bool updown_button_repeat; /* If up/down button being used to scroll, auto-repeat? */
+ Bool leftright_button_repeat; /* If left/right button being used to scroll, auto-repeat? */
+ int scroll_button_repeat; /* time, in milliseconds, between scroll events being
+ * sent when holding down scroll buttons */
+ int touchpad_off; /* Switches the touchpad off
+ * 0 : Not off
+ * 1 : Off
+ * 2 : Only tapping and scrolling off
+ */
+ Bool locked_drags; /* Enable locked drags */
+ int locked_drag_time; /* timeout for locked drags */
+ int tap_action[MAX_TAP]; /* Button to report on tap events */
+ int click_action[MAX_CLICK]; /* Button to report on click with fingers */
+ Bool circular_scrolling; /* Enable circular scrolling */
+ double scroll_dist_circ; /* Scrolling angle radians */
+ int circular_trigger; /* Trigger area for circular scrolling */
+ Bool circular_pad; /* Edge has an oval or circular shape */
+ Bool palm_detect; /* Enable Palm Detection */
+ int palm_min_width; /* Palm detection width */
+ int palm_min_z; /* Palm detection depth */
+ double coasting_speed; /* Coasting threshold scrolling speed in scrolls/s */
+ double coasting_friction; /* Number of scrolls per second per second to change coasting speed */
+ int press_motion_min_z; /* finger pressure at which minimum pressure motion factor is applied */
+ int press_motion_max_z; /* finger pressure at which maximum pressure motion factor is applied */
+ double press_motion_min_factor; /* factor applied on speed when finger pressure is at minimum */
+ double press_motion_max_factor; /* factor applied on speed when finger pressure is at minimum */
+ Bool grab_event_device; /* grab event device for exclusive use? */
+ Bool tap_and_drag_gesture; /* Switches the tap-and-drag gesture on/off */
+ unsigned int resolution_horiz; /* horizontal resolution of touchpad in units/mm */
+ unsigned int resolution_vert; /* vertical resolution of touchpad in units/mm */
+ int area_left_edge, area_right_edge, area_top_edge, area_bottom_edge; /* area coordinates absolute */
+ int softbutton_areas[2][4]; /* soft button area coordinates, 0 => right, 1 => middle button */
+ int hyst_x, hyst_y; /* x and y width of hysteresis box */
+} SynapticsParameters;
+
+struct _SynapticsPrivateRec {
+ SynapticsParameters synpara; /* Default parameter settings, read from
+ the X config file */
+ SynapticsSHM *synshm; /* Current parameter settings. Will point to
+ shared memory if shm_config is true */
+ struct SynapticsProtocolOperations *proto_ops;
+ void *proto_data; /* protocol-specific data */
+
+ struct SynapticsHwState *hwState;
+ struct SynapticsHwState *old_hw_state; /* previous logical hw state */
+
+ const char *device; /* device node */
+ Bool shm_config; /* True when shared memory area allocated */
+
+ CARD32 timer_time; /* when timer last fired */
+ OsTimerPtr timer; /* for up/down-button repeat, tap processing, etc */
+
+ struct CommData comm;
+
+ struct SynapticsHwState *local_hw_state; /* used in place of local hw state variables */
+
+ Bool absolute_events; /* post absolute motion events instead of relative */
+ SynapticsMoveHistRec move_hist[SYNAPTICS_MOVE_HISTORY]; /* movement history */
+ int hist_index; /* Last added entry in move_hist[] */
+ int hyst_center_x; /* center x of hysteresis */
+ int hyst_center_y; /* center y of hysteresis */
+ struct {
+ int last_x; /* last x-scroll position */
+ int last_y; /* last y-scroll position */
+ double delta_x; /* accumulated horiz scroll delta */
+ double delta_y; /* accumulated vert scroll delta */
+ double last_a; /* last angle-scroll position */
+ CARD32 last_millis; /* time last scroll event posted */
+ double coast_speed_x; /* Horizontal coasting speed in scrolls/s */
+ double coast_speed_y; /* Vertical coasting speed in scrolls/s */
+ double coast_delta_x; /* Accumulated horizontal coast delta */
+ double coast_delta_y; /* Accumulated vertical coast delta */
+ int packets_this_scroll; /* Events received for this scroll */
+ } scroll;
+ int count_packet_finger; /* packet counter with finger on the touchpad */
+ int button_delay_millis; /* button delay for 3rd button emulation */
+ Bool prev_up; /* Previous up button value, for double click emulation */
+ enum FingerState finger_state; /* previous finger state */
+ CARD32 last_motion_millis; /* time of the last motion */
+
+ enum TapState tap_state; /* State of tap processing */
+ int tap_max_fingers; /* Max number of fingers seen since entering start state */
+ int tap_button; /* Which button started the tap processing */
+ enum TapButtonState tap_button_state; /* Current tap action */
+ SynapticsMoveHistRec touch_on; /* data when the touchpad is touched/released */
+
+ enum MovingState moving_state; /* previous moving state */
+ Bool vert_scroll_edge_on; /* Keeps track of currently active scroll modes */
+ Bool horiz_scroll_edge_on; /* Keeps track of currently active scroll modes */
+ Bool vert_scroll_twofinger_on; /* Keeps track of currently active scroll modes */
+ Bool horiz_scroll_twofinger_on; /* Keeps track of currently active scroll modes */
+ Bool circ_scroll_on; /* Keeps track of currently active scroll modes */
+ Bool circ_scroll_vert; /* True: Generate vertical scroll events
+ False: Generate horizontal events */
+ int trackstick_neutral_x; /* neutral x position for trackstick mode */
+ int trackstick_neutral_y; /* neutral y position for trackstick mode */
+ double frac_x, frac_y; /* absolute -> relative fraction */
+ enum MidButtonEmulation mid_emu_state; /* emulated 3rd button */
+ int repeatButtons; /* buttons for repeat */
+ int nextRepeat; /* Time when to trigger next auto repeat event */
+ int lastButtons; /* last state of the buttons */
+ int prev_z; /* previous z value, for palm detection */
+ int prevFingers; /* previous numFingers, for transition detection */
+ int avg_width; /* weighted average of previous fingerWidth values */
+ double horiz_coeff; /* normalization factor for x coordintes */
+ double vert_coeff; /* normalization factor for y coordintes */
+
+ int minx, maxx, miny, maxy; /* min/max dimensions as detected */
+ int minp, maxp, minw, maxw; /* min/max pressure and finger width as detected */
+ int resx, resy; /* resolution of coordinates as detected in units/mm */
+ Bool has_left; /* left button detected for this device */
+ Bool has_right; /* right button detected for this device */
+ Bool has_middle; /* middle button detected for this device */
+ Bool has_double; /* double click detected for this device */
+ Bool has_triple; /* triple click detected for this device */
+ Bool has_pressure; /* device reports pressure */
+ Bool has_width; /* device reports finger width */
+ Bool has_scrollbuttons; /* device has physical scrollbuttons */
+ Bool has_semi_mt; /* device is only semi-multitouch capable */
+
+ enum TouchpadModel model; /* The detected model */
+ unsigned short id_vendor; /* vendor id */
+ unsigned short id_product; /* product id */
+
+#ifdef HAVE_SMOOTH_SCROLL
+ int scroll_axis_horiz; /* Horizontal smooth-scrolling axis */
+ int scroll_axis_vert; /* Vertical smooth-scrolling axis */
+ ValuatorMask *scroll_events_mask; /* ValuatorMask for smooth-scrolling */
+#endif
+
+#ifdef HAVE_MULTITOUCH
+ Bool has_touch; /* Device has multitouch capabilities */
+ int max_touches; /* Number of touches supported */
+ int num_mt_axes; /* Number of multitouch axes other than X, Y */
+ SynapticsTouchAxisRec *touch_axes; /* Touch axis information other than X, Y */
+ int num_slots; /* Number of touch slots allocated */
+ int *open_slots; /* Array of currently open touch slots */
+ int num_active_touches; /* Number of active touches on device */
+#endif
+};
+
+#endif /* _SYNAPTICSSTR_H_ */
diff --git a/src/synproto.c b/src/synproto.c
new file mode 100644
index 0000000..0c8a066
--- /dev/null
+++ b/src/synproto.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright © 2012 Canonical, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "synproto.h"
+#include "synaptics.h"
+#include "synapticsstr.h"
+
+#ifdef HAVE_MULTITOUCH
+static int
+HwStateAllocTouch(struct SynapticsHwState *hw, SynapticsPrivate * priv)
+{
+ int num_vals;
+ int i = 0;
+
+ hw->num_mt_mask = priv->num_slots;
+ hw->mt_mask = malloc(hw->num_mt_mask * sizeof(ValuatorMask *));
+ if (!hw->mt_mask)
+ goto fail;
+
+ num_vals = 2; /* x and y */
+ num_vals += 2; /* scroll axes */
+ num_vals += priv->num_mt_axes;
+
+ for (; i < hw->num_mt_mask; i++) {
+ hw->mt_mask[i] = valuator_mask_new(num_vals);
+ if (!hw->mt_mask[i])
+ goto fail;
+ }
+
+ hw->slot_state = calloc(hw->num_mt_mask, sizeof(enum SynapticsSlotState));
+ if (!hw->slot_state)
+ goto fail;
+
+ return Success;
+
+ fail:
+ for (i--; i >= 0; i--)
+ valuator_mask_free(&hw->mt_mask[i]);
+ free(hw->mt_mask);
+ hw->mt_mask = NULL;
+ return BadAlloc;
+}
+#endif
+
+struct SynapticsHwState *
+SynapticsHwStateAlloc(SynapticsPrivate * priv)
+{
+ struct SynapticsHwState *hw;
+
+ hw = calloc(1, sizeof(struct SynapticsHwState));
+ if (!hw)
+ return NULL;
+
+#ifdef HAVE_MULTITOUCH
+ if (HwStateAllocTouch(hw, priv) != Success) {
+ free(hw);
+ return NULL;
+ }
+#endif
+
+ return hw;
+}
+
+void
+SynapticsHwStateFree(struct SynapticsHwState **hw)
+{
+#ifdef HAVE_MULTITOUCH
+ int i;
+
+ if (!*hw)
+ return;
+
+ free((*hw)->slot_state);
+ for (i = 0; i < (*hw)->num_mt_mask; i++)
+ valuator_mask_free(&(*hw)->mt_mask[i]);
+ free((*hw)->mt_mask);
+#endif
+
+ free(*hw);
+ *hw = NULL;
+}
+
+void
+SynapticsCopyHwState(struct SynapticsHwState *dst,
+ const struct SynapticsHwState *src)
+{
+#ifdef HAVE_MULTITOUCH
+ int i;
+#endif
+
+ dst->millis = src->millis;
+ dst->x = src->x;
+ dst->y = src->y;
+ dst->z = src->z;
+ dst->cumulative_dx = src->cumulative_dx;
+ dst->cumulative_dy = src->cumulative_dy;
+ dst->numFingers = src->numFingers;
+ dst->fingerWidth = src->fingerWidth;
+ dst->left = src->left & BTN_EMULATED_FLAG ? 0 : src->left;
+ dst->right = src->right & BTN_EMULATED_FLAG ? 0 : src->right;
+ dst->up = src->up;
+ dst->down = src->down;
+ memcpy(dst->multi, src->multi, sizeof(dst->multi));
+ dst->middle = src->middle & BTN_EMULATED_FLAG ? 0 : src->middle;
+#ifdef HAVE_MULTITOUCH
+ for (i = 0; i < dst->num_mt_mask && i < src->num_mt_mask; i++)
+ valuator_mask_copy(dst->mt_mask[i], src->mt_mask[i]);
+ memcpy(dst->slot_state, src->slot_state,
+ dst->num_mt_mask * sizeof(enum SynapticsSlotState));
+#endif
+}
+
+void
+SynapticsResetHwState(struct SynapticsHwState *hw)
+{
+ hw->millis = 0;
+ hw->x = INT_MIN;
+ hw->y = INT_MIN;
+ hw->z = 0;
+ hw->cumulative_dx = 0;
+ hw->cumulative_dy = 0;
+ hw->numFingers = 0;
+ hw->fingerWidth = 0;
+
+ hw->left = 0;
+ hw->right = 0;
+ hw->up = 0;
+ hw->down = 0;
+
+ hw->middle = 0;
+ memset(hw->multi, 0, sizeof(hw->multi));
+
+ SynapticsResetTouchHwState(hw, TRUE);
+}
+
+void
+SynapticsResetTouchHwState(struct SynapticsHwState *hw, Bool set_slot_empty)
+{
+#ifdef HAVE_MULTITOUCH
+ int i;
+
+ for (i = 0; i < hw->num_mt_mask; i++) {
+ int j;
+
+ /* Leave x and y valuators in case we need to restart touch */
+ for (j = 2; j < valuator_mask_num_valuators(hw->mt_mask[i]); j++)
+ valuator_mask_unset(hw->mt_mask[i], j);
+
+ switch (hw->slot_state[i]) {
+ case SLOTSTATE_OPEN:
+ case SLOTSTATE_OPEN_EMPTY:
+ case SLOTSTATE_UPDATE:
+ hw->slot_state[i] =
+ set_slot_empty ? SLOTSTATE_EMPTY : SLOTSTATE_OPEN_EMPTY;
+ break;
+
+ default:
+ hw->slot_state[i] = SLOTSTATE_EMPTY;
+ break;
+ }
+ }
+#endif
+}
diff --git a/src/synproto.h b/src/synproto.h
new file mode 100644
index 0000000..a099fce
--- /dev/null
+++ b/src/synproto.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright © 2004 Peter Osterlund
+ *
+ * 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 Osterlund (petero2@telia.com)
+ */
+
+#ifndef _SYNPROTO_H_
+#define _SYNPROTO_H_
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <xf86Xinput.h>
+#include <xisb.h>
+
+#include "config.h"
+
+struct _SynapticsPrivateRec;
+typedef struct _SynapticsPrivateRec SynapticsPrivate;
+
+enum SynapticsSlotState {
+ SLOTSTATE_EMPTY = 0,
+ SLOTSTATE_OPEN,
+ SLOTSTATE_CLOSE,
+ SLOTSTATE_OPEN_EMPTY,
+ SLOTSTATE_UPDATE,
+};
+
+/* used to mark emulated hw button state */
+#define BTN_EMULATED_FLAG 0x80
+
+/*
+ * A structure to describe the state of the touchpad hardware (buttons and pad)
+ */
+struct SynapticsHwState {
+ CARD32 millis; /* Timestamp in milliseconds */
+ int x; /* X position of finger */
+ int y; /* Y position of finger */
+ int z; /* Finger pressure */
+ int cumulative_dx; /* Cumulative delta X for clickpad dragging */
+ int cumulative_dy; /* Cumulative delta Y for clickpad dragging */
+ int numFingers;
+ int fingerWidth;
+
+ Bool left;
+ Bool right;
+ Bool up;
+ Bool down;
+
+ Bool multi[8];
+ Bool middle; /* Some ALPS touchpads have a middle button */
+
+#ifdef HAVE_MULTITOUCH
+ int num_mt_mask;
+ ValuatorMask **mt_mask;
+ enum SynapticsSlotState *slot_state;
+#endif
+};
+
+struct CommData {
+ XISBuffer *buffer;
+ unsigned char protoBuf[6]; /* Buffer for Packet */
+ unsigned char lastByte; /* Last read byte. Use for reset sequence detection. */
+ int outOfSync; /* How many consecutive incorrect packets we
+ have received */
+ int protoBufTail;
+
+ /* Used for keeping track of partial HwState updates. */
+ struct SynapticsHwState *hwState;
+ Bool oneFinger;
+ Bool twoFingers;
+ Bool threeFingers;
+};
+
+struct _SynapticsParameters;
+
+struct SynapticsProtocolOperations {
+ Bool (*DeviceOnHook) (InputInfoPtr pInfo,
+ struct _SynapticsParameters * para);
+ Bool (*DeviceOffHook) (InputInfoPtr pInfo);
+ Bool (*QueryHardware) (InputInfoPtr pInfo);
+ Bool (*ReadHwState) (InputInfoPtr pInfo,
+ struct CommData * comm,
+ struct SynapticsHwState * hwRet);
+ Bool (*AutoDevProbe) (InputInfoPtr pInfo, const char *device);
+ void (*ReadDevDimensions) (InputInfoPtr pInfo);
+};
+
+#ifdef BUILD_PS2COMM
+extern struct SynapticsProtocolOperations psaux_proto_operations;
+extern struct SynapticsProtocolOperations alps_proto_operations;
+#endif /* BUILD_PS2COMM */
+#ifdef BUILD_EVENTCOMM
+extern struct SynapticsProtocolOperations event_proto_operations;
+#endif /* BUILD_EVENTCOMM */
+#ifdef BUILD_PSMCOMM
+extern struct SynapticsProtocolOperations psm_proto_operations;
+#endif /* BUILD_PSMCOMM */
+
+extern struct SynapticsHwState *SynapticsHwStateAlloc(SynapticsPrivate * priv);
+extern void SynapticsHwStateFree(struct SynapticsHwState **hw);
+extern void SynapticsCopyHwState(struct SynapticsHwState *dst,
+ const struct SynapticsHwState *src);
+extern void SynapticsResetHwState(struct SynapticsHwState *hw);
+extern void SynapticsResetTouchHwState(struct SynapticsHwState *hw,
+ Bool force_empty);
+
+extern Bool SynapticsIsSoftButtonAreasValid(int *values);
+
+#endif /* _SYNPROTO_H_ */
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644
index 0000000..a59f751
--- /dev/null
+++ b/test/.gitignore
@@ -0,0 +1,6 @@
+# Add & Override patterns for xf86-input-synaptics
+#
+# Edit the following section as needed
+# For example, !report.pc overrides *.pc. See 'man gitignore'
+
+eventcomm-test
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 0000000..6fbbd23
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,18 @@
+if ENABLE_UNIT_TESTS
+AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/include
+AM_CFLAGS = $(XORG_CFLAGS) $(CWARNFLAGS)
+fake_syms = fake-symbols.c fake-symbols.h
+
+if BUILD_EVENTCOMM
+noinst_PROGRAMS = eventcomm-test
+
+eventcomm_test_SOURCES = eventcomm-test.c\
+ $(top_srcdir)/src/eventcomm.c \
+ $(top_srcdir)/src/synproto.c \
+ $(fake_syms)
+
+eventcomm_test_LDADD = $(MTDEV_LIBS)
+endif
+
+TESTS = $(noinst_PROGRAMS)
+endif
diff --git a/test/eventcomm-test.c b/test/eventcomm-test.c
new file mode 100644
index 0000000..d471e98
--- /dev/null
+++ b/test/eventcomm-test.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright © 2011 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
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "synaptics.h"
+#include "synapticsstr.h"
+#include "eventcomm.h"
+
+#define ArrayLength(a) (sizeof(a) / (sizeof((a)[0])))
+
+int fd_read, fd_write;
+
+/* A syn event, always handy to have */
+struct input_event syn = { {0, 0}, EV_SYN, SYN_REPORT, 0 };
+
+static void
+create_pipe_fd(void)
+{
+ int pipefd[2];
+
+ assert(pipe(pipefd) != -1);
+
+ fd_read = pipefd[0];
+ fd_write = pipefd[1];
+}
+
+static void
+reset_data(struct SynapticsHwState **hw, struct CommData *comm,
+ SynapticsPrivate * priv)
+{
+ SynapticsHwStateFree(&comm->hwState);
+ memset(comm, 0, sizeof(struct CommData));
+ SynapticsHwStateFree(hw);
+ *hw = SynapticsHwStateAlloc(priv);
+ comm->hwState = SynapticsHwStateAlloc(priv);
+}
+
+/**
+ * Write n input events to fd, followed by the syn event.
+ */
+static void
+write_event(int fd, struct input_event *ev, int n)
+{
+ write(fd, ev, sizeof(struct input_event) * n);
+ write(fd, &syn, sizeof(syn));
+}
+
+static void
+test_buttons(int fd, InputInfoPtr pInfo, struct CommData *comm)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ struct SynapticsHwState *hw = NULL;
+ struct input_event ev = { {0, 0}, EV_KEY, 0, 0 };
+
+ reset_data(&hw, comm, priv);
+
+#define _test_press_release(_code, field) \
+ ev.code = (_code); \
+ ev.value = 1; \
+ write_event(fd, &ev, 1); \
+ EventReadHwState(pInfo, comm, hw); \
+ assert(hw->field == 1); \
+ ev.value = 0; /* button release */ \
+ write_event(fd_write, &ev, 1); \
+ EventReadHwState(pInfo, comm, hw); \
+ assert(hw->field == 0);
+
+ _test_press_release(BTN_LEFT, left);
+ _test_press_release(BTN_RIGHT, right);
+ _test_press_release(BTN_MIDDLE, middle);
+ _test_press_release(BTN_FORWARD, up);
+ _test_press_release(BTN_BACK, down);
+ _test_press_release(BTN_0, multi[0]);
+ _test_press_release(BTN_1, multi[1]);
+ _test_press_release(BTN_2, multi[2]);
+ _test_press_release(BTN_3, multi[3]);
+ _test_press_release(BTN_4, multi[4]);
+ _test_press_release(BTN_5, multi[5]);
+ _test_press_release(BTN_6, multi[6]);
+ _test_press_release(BTN_7, multi[7]);
+
+ SynapticsHwStateFree(&hw);
+}
+
+/**
+ * This test checks that the recognised event fields set the right hardware
+ * state. It's a fairly limited test and does not check whether any of the
+ * others change the HW state at all.
+ */
+static void
+test_read_hw_state(void)
+{
+ InputInfoRec info = { 0 };
+ SynapticsPrivate private;
+ struct SynapticsHwState *hw = NULL;
+ struct CommData comm = { 0 };
+
+ struct input_event ev[] = {
+ {{0, 0}, EV_KEY, BTN_TOOL_FINGER, 1},
+ {{0, 0}, EV_KEY, BTN_TOOL_DOUBLETAP, 1},
+ {{0, 0}, EV_KEY, BTN_TOOL_TRIPLETAP, 1},
+ {{0, 0}, EV_ABS, ABS_X, 42},
+ {{0, 0}, EV_ABS, ABS_Y, 21},
+ {{0, 0}, EV_ABS, ABS_PRESSURE, 56},
+ {{0, 0}, EV_ABS, ABS_TOOL_WIDTH, 204},
+ };
+
+ memset(&private, 0, sizeof(private));
+
+ info.private = &private;
+ info.fd = fd_read;
+
+ private.proto_data = EventProtoDataAlloc();
+
+ /* just the syn event */
+ reset_data(&hw, &comm, &private);
+ write(fd_write, &syn, sizeof(syn));
+ EventReadHwState(&info, &comm, hw);
+ assert(hw->numFingers == 0);
+
+ /* one finger */
+ reset_data(&hw, &comm, &private);
+ write_event(fd_write, &ev[0], 1);
+ EventReadHwState(&info, &comm, hw);
+ assert(hw->numFingers == 1);
+
+ /* two fingers */
+ reset_data(&hw, &comm, &private);
+ write_event(fd_write, &ev[1], 1);
+ EventReadHwState(&info, &comm, hw);
+ assert(hw->numFingers == 2);
+
+ /* three fingers */
+ reset_data(&hw, &comm, &private);
+ write_event(fd_write, &ev[2], 1);
+ EventReadHwState(&info, &comm, hw);
+ assert(hw->numFingers == 3);
+
+ /* x/y data */
+ reset_data(&hw, &comm, &private);
+ write_event(fd_write, &ev[3], 2);
+ EventReadHwState(&info, &comm, hw);
+ assert(hw->x == ev[3].value);
+ assert(hw->y == ev[4].value);
+
+ /* pressure */
+ reset_data(&hw, &comm, &private);
+ write_event(fd_write, &ev[5], 1);
+ EventReadHwState(&info, &comm, hw);
+ assert(hw->z == ev[5].value);
+
+ /* finger width */
+ reset_data(&hw, &comm, &private);
+ write_event(fd_write, &ev[6], 1);
+ EventReadHwState(&info, &comm, hw);
+ assert(hw->fingerWidth == ev[6].value);
+
+ /* the various buttons */
+ test_buttons(fd_write, &info, &comm);
+
+ free(private.proto_data);
+ SynapticsHwStateFree(&hw);
+ SynapticsHwStateFree(&comm.hwState);
+}
+
+static Bool
+compare_hw_state(const struct SynapticsHwState *a,
+ const struct SynapticsHwState *b)
+{
+#define COMPARE(x) \
+ if (a->x != b->x) return a->x - b->x
+
+ COMPARE(millis);
+ COMPARE(x);
+ COMPARE(y);
+ COMPARE(z);
+ COMPARE(numFingers);
+ COMPARE(fingerWidth);
+ COMPARE(left);
+ COMPARE(right);
+ COMPARE(up);
+ COMPARE(down);
+ if (memcmp(a->multi, b->multi, sizeof(a->multi)))
+ return memcmp(a->multi, b->multi, sizeof(a->multi));
+ COMPARE(middle);
+
+#undef COMPARE
+
+ return 0;
+}
+
+/**
+ * Make sure that axes/keys unknown to the driver don't change the hardware
+ * state.
+ */
+static void
+test_ignore_hw_state(void)
+{
+ int i;
+ InputInfoRec info = { 0 };
+ SynapticsPrivate private;
+ struct SynapticsHwState *hw = NULL;
+ struct SynapticsHwState *hw_zero = NULL;
+ struct CommData comm = { 0 };
+
+ int known_abs[] = {
+ ABS_X,
+ ABS_Y,
+ ABS_PRESSURE,
+ ABS_TOOL_WIDTH,
+ };
+
+ int known_keys[] = {
+ BTN_LEFT,
+ BTN_RIGHT,
+ BTN_MIDDLE,
+ BTN_FORWARD,
+ BTN_BACK,
+ BTN_0,
+ BTN_1,
+ BTN_2,
+ BTN_3,
+ BTN_4,
+ BTN_5,
+ BTN_6,
+ BTN_7,
+ BTN_TOOL_FINGER,
+ BTN_TOOL_DOUBLETAP,
+ BTN_TOOL_TRIPLETAP,
+ BTN_TOUCH
+ };
+
+ struct input_event ev = { {0, 0}, 0, 0, 1 };
+
+ memset(&private, 0, sizeof(private));
+ info.private = &private;
+ info.fd = fd_read;
+
+ private.proto_data = EventProtoDataAlloc();
+
+ reset_data(&hw_zero, &comm, &private);
+
+#define _assert_no_change(_type, _code) \
+ reset_data(&hw, &comm, &private); \
+ ev.type = _type; \
+ ev.code = _code; \
+ ev.value = 1; \
+ write_event(fd_write, &ev, 1); \
+ EventReadHwState(&info, &comm, hw); \
+ assert(compare_hw_state(hw, hw_zero) == 0);
+
+ for (i = ABS_X; i < ABS_MAX; i++) {
+ int j, skip = 0;
+
+ for (j = 0; j < ArrayLength(known_abs); j++) {
+ if (i == known_abs[j]) {
+ skip = 1;
+ break;
+ }
+ }
+
+ if (skip)
+ continue;
+
+ _assert_no_change(EV_ABS, i);
+ }
+
+ for (i = KEY_RESERVED; i < KEY_MAX; i++) {
+ int j, skip = 0;
+
+ for (j = 0; j < ArrayLength(known_keys); j++) {
+ if (i == known_keys[j]) {
+ skip = 1;
+ break;
+ }
+ }
+
+ if (skip)
+ continue;
+
+ _assert_no_change(EV_KEY, i);
+ }
+
+ free(private.proto_data);
+ SynapticsHwStateFree(&hw);
+ SynapticsHwStateFree(&hw_zero);
+ SynapticsHwStateFree(&comm.hwState);
+}
+
+int
+main(int argc, char **argv)
+{
+ create_pipe_fd();
+
+ test_read_hw_state();
+ test_ignore_hw_state();
+ return 0;
+}
diff --git a/test/fake-symbols.c b/test/fake-symbols.c
new file mode 100644
index 0000000..6f55c7b
--- /dev/null
+++ b/test/fake-symbols.c
@@ -0,0 +1,475 @@
+#include "fake-symbols.h"
+
+_X_EXPORT
+ int
+xf86ReadSerial(int fd, void *buf, int count)
+{
+ return 0;
+}
+
+_X_EXPORT int
+xf86WriteSerial(int fd, const void *buf, int count)
+{
+ return 0;
+}
+
+_X_EXPORT int
+xf86CloseSerial(int fd)
+{
+ return 0;
+}
+
+_X_EXPORT int
+xf86WaitForInput(int fd, int timeout)
+{
+ return 0;
+}
+
+_X_EXPORT int
+xf86OpenSerial(OPTTYPE options)
+{
+ return 0;
+}
+
+_X_EXPORT int
+xf86SetSerialSpeed(int fd, int speed)
+{
+ return 0;
+}
+
+_X_EXPORT OPTTYPE
+xf86ReplaceIntOption(OPTTYPE optlist, const char *name, const int val)
+{
+ return NULL;
+}
+
+_X_EXPORT char *
+xf86SetStrOption(OPTTYPE optlist, const char *name, CONST char *deflt)
+{
+ return NULL;
+}
+
+_X_EXPORT int
+xf86SetBoolOption(OPTTYPE optlist, const char *name, int deflt)
+{
+ return 0;
+}
+
+_X_EXPORT OPTTYPE
+xf86AddNewOption(OPTTYPE head, const char *name, const char *val)
+{
+ return NULL;
+}
+
+_X_EXPORT CONST char *
+xf86FindOptionValue(OPTTYPE options, const char *name)
+{
+ return NULL;
+}
+
+_X_EXPORT char *
+xf86OptionName(OPTTYPE opt)
+{
+ return NULL;
+}
+
+_X_EXPORT char *
+xf86OptionValue(OPTTYPE opt)
+{
+ return NULL;
+}
+
+_X_EXPORT int
+xf86NameCmp(const char *s1, const char *s2)
+{
+ return 0;
+}
+
+_X_EXPORT char *
+xf86CheckStrOption(OPTTYPE optlist, const char *name, char *deflt)
+{
+ return NULL;
+}
+
+_X_EXPORT void
+xf86AddEnabledDevice(InputInfoPtr pInfo)
+{
+ return;
+}
+
+_X_EXPORT void
+xf86RemoveEnabledDevice(InputInfoPtr pInfo)
+{
+ return;
+}
+
+_X_EXPORT Atom
+XIGetKnownProperty(char *name)
+{
+ return None;
+}
+
+_X_EXPORT void
+xf86AddInputDriver(InputDriverPtr driver, pointer module, int flags)
+{
+ return;
+}
+
+_X_EXPORT int
+xf86ScaleAxis(int Cx, int to_max, int to_min, int from_max, int from_min)
+{
+ int X;
+ int64_t to_width = to_max - to_min;
+ int64_t from_width = from_max - from_min;
+
+ if (from_width) {
+ X = (int) (((to_width * (Cx - from_min)) / from_width) + to_min);
+ }
+ else {
+ X = 0;
+ /*ErrorF ("Divide by Zero in xf86ScaleAxis\n"); */
+ }
+
+ if (X > to_max)
+ X = to_max;
+ if (X < to_min)
+ X = to_min;
+
+ return X;
+}
+
+_X_EXPORT void
+DeleteInputDeviceRequest(DeviceIntPtr pDev)
+{
+ return;
+}
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+_X_EXPORT void
+FreeInputAttributes(InputAttributes * attrs)
+{
+ return;
+}
+#endif
+
+_X_EXPORT void
+xf86PostButtonEvent(DeviceIntPtr device,
+ int is_absolute,
+ int button,
+ int is_down, int first_valuator, int num_valuators, ...)
+{
+ return;
+}
+
+_X_EXPORT int
+Xasprintf(char **ret, const char *format, ...)
+{
+ return 0;
+}
+
+_X_EXPORT int
+XISetDevicePropertyDeletable(DeviceIntPtr dev, Atom property, Bool deletable)
+{
+ return 0;
+}
+
+_X_EXPORT InputInfoPtr
+xf86FirstLocalDevice(void)
+{
+ return NULL;
+}
+
+_X_EXPORT void
+xf86DeleteInput(InputInfoPtr pInp, int flags)
+{
+ return;
+}
+
+_X_EXPORT OPTTYPE
+xf86OptionListDuplicate(OPTTYPE options)
+{
+ return NULL;
+}
+
+_X_EXPORT Bool
+InitButtonClassDeviceStruct(DeviceIntPtr dev, int numButtons, Atom *labels,
+ CARD8 *map)
+{
+ return FALSE;
+}
+
+_X_EXPORT void
+InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
+ int maxval, int resolution, int min_res, int max_res,
+ int mode)
+{
+ return;
+}
+
+_X_EXPORT void
+xf86PostKeyboardEvent(DeviceIntPtr device, unsigned int key_code, int is_down)
+{
+ return;
+}
+
+_X_EXPORT int
+xf86SetIntOption(OPTTYPE optlist, const char *name, int deflt)
+{
+ return 0;
+}
+
+_X_EXPORT void
+xf86PostButtonEventP(DeviceIntPtr device,
+ int is_absolute,
+ int button,
+ int is_down, int first_valuator, int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators)
+{
+ return;
+}
+
+_X_EXPORT Bool
+InitPtrFeedbackClassDeviceStruct(DeviceIntPtr dev, PtrCtrlProcPtr controlProc)
+{
+ return FALSE;
+}
+
+_X_EXPORT int
+XIChangeDeviceProperty(DeviceIntPtr dev, Atom property, Atom type,
+ int format, int mode, unsigned long len,
+ OPTTYPE value, Bool sendevent)
+{
+ return 0;
+}
+
+_X_EXPORT CARD32
+GetTimeInMillis(void)
+{
+ return 0;
+}
+
+_X_EXPORT int
+NewInputDeviceRequest(InputOption *options,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+ InputAttributes * attrs,
+#endif
+ DeviceIntPtr *pdev)
+{
+ return 0;
+}
+
+_X_EXPORT Bool
+InitLedFeedbackClassDeviceStruct(DeviceIntPtr dev, LedCtrlProcPtr controlProc)
+{
+ return FALSE;
+}
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+_X_EXPORT InputAttributes *
+DuplicateInputAttributes(InputAttributes * attrs)
+{
+ return NULL;
+}
+#endif
+
+_X_EXPORT int
+ValidAtom(Atom atom)
+{
+ return None;
+}
+
+_X_EXPORT Bool
+InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
+ BellProcPtr bell_func, KbdCtrlProcPtr ctrl_func)
+{
+ return FALSE;
+}
+
+_X_EXPORT long
+XIRegisterPropertyHandler(DeviceIntPtr dev,
+ int (*SetProperty) (DeviceIntPtr dev,
+ Atom property,
+ XIPropertyValuePtr prop,
+ BOOL checkonly),
+ int (*GetProperty) (DeviceIntPtr dev,
+ Atom property),
+ int (*DeleteProperty) (DeviceIntPtr dev,
+ Atom property))
+{
+ return 0;
+}
+
+_X_EXPORT int
+InitProximityClassDeviceStruct(DeviceIntPtr dev)
+{
+ return 0;
+}
+
+_X_EXPORT void
+xf86Msg(MessageType type, const char *format, ...)
+{
+ return;
+}
+
+_X_EXPORT void
+xf86MsgVerb(MessageType type, int verb, const char *format, ...)
+{
+ return;
+}
+
+_X_EXPORT void
+xf86IDrvMsg(InputInfoPtr dev, MessageType type, const char *format, ...)
+{
+ return;
+}
+
+_X_EXPORT void
+xf86PostMotionEventP(DeviceIntPtr device,
+ int is_absolute, int first_valuator, int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators)
+{
+ return;
+}
+
+_X_EXPORT Bool
+InitValuatorClassDeviceStruct(DeviceIntPtr dev, int numAxes, Atom *labels,
+ int numMotionEvents, int mode)
+{
+ return FALSE;
+}
+
+_X_EXPORT OPTTYPE
+xf86ReplaceStrOption(OPTTYPE optlist, const char *name, const char *val)
+{
+ return NULL;
+}
+
+_X_EXPORT OPTTYPE
+xf86NextOption(OPTTYPE list)
+{
+ return NULL;
+}
+
+_X_EXPORT int
+XIGetDeviceProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr *value)
+{
+ return 0;
+}
+
+_X_EXPORT Atom
+MakeAtom(const char *string, unsigned len, Bool makeit)
+{
+ return None;
+}
+
+_X_EXPORT int
+GetMotionHistorySize(void)
+{
+ return 0;
+}
+
+_X_EXPORT void
+xf86PostProximityEventP(DeviceIntPtr device,
+ int is_in, int first_valuator, int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators)
+{
+ return;
+}
+
+_X_EXPORT Bool
+InitFocusClassDeviceStruct(DeviceIntPtr dev)
+{
+ return FALSE;
+}
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12
+void
+xf86ProcessCommonOptions(InputInfoPtr pInfo, OPTTYPE list)
+{
+}
+
+void
+xf86CollectInputOptions(InputInfoPtr pInfo,
+ const char **defaultOpts, OPTTYPE extraOpts)
+{
+}
+
+InputInfoPtr
+xf86AllocateInput(InputDriverPtr drv, int flags)
+{
+ return NULL;
+}
+
+#endif
+
+ClientPtr serverClient;
+
+Bool
+QueueWorkProc(Bool (*function)
+ (ClientPtr /* pClient */ , pointer /* closure */ ),
+ ClientPtr client, pointer closure)
+{
+ return FALSE;
+}
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+_X_EXPORT ValuatorMask *
+valuator_mask_new(int num_valuators)
+{
+ return NULL;
+}
+
+_X_EXPORT void
+valuator_mask_free(ValuatorMask **mask)
+{
+}
+
+_X_EXPORT int
+valuator_mask_get(const ValuatorMask *mask, int valuator)
+{
+ return 0;
+}
+
+_X_EXPORT void
+valuator_mask_set(ValuatorMask *mask, int valuator, int data)
+{
+}
+
+extern _X_EXPORT void
+valuator_mask_unset(ValuatorMask *mask, int bit)
+{
+}
+
+_X_EXPORT int
+valuator_mask_num_valuators(const ValuatorMask *mask)
+{
+ return 0;
+}
+
+_X_EXPORT void
+valuator_mask_zero(ValuatorMask *mask)
+{
+}
+
+_X_EXPORT void
+valuator_mask_copy(ValuatorMask *dest, const ValuatorMask *src)
+{
+}
+#endif
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 16
+_X_EXPORT void
+xf86PostTouchEvent(DeviceIntPtr dev, uint32_t touchid,
+ uint16_t type, uint32_t flags, const ValuatorMask *mask)
+{
+}
+#endif
diff --git a/test/fake-symbols.h b/test/fake-symbols.h
new file mode 100644
index 0000000..70049d0
--- /dev/null
+++ b/test/fake-symbols.h
@@ -0,0 +1,187 @@
+#include <xorg-server.h>
+#include <xf86Xinput.h>
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 14
+#define OPTTYPE XF86OptionPtr
+#define CONST const
+#else
+#define OPTTYPE pointer
+#define CONST
+#endif
+
+extern int xf86ReadSerial(int fd, void *buf, int count);
+extern int xf86WriteSerial(int fd, const void *buf, int count);
+extern int xf86CloseSerial(int fd);
+extern int xf86WaitForInput(int fd, int timeout);
+extern int xf86OpenSerial(OPTTYPE options);
+extern int xf86SetSerialSpeed(int fd, int speed);
+
+extern OPTTYPE xf86ReplaceIntOption(OPTTYPE optlist, const char *name,
+ const int val);
+extern OPTTYPE xf86AddNewOption(OPTTYPE head, const char *name,
+ const char *val);
+extern char *xf86OptionName(OPTTYPE opt);
+extern CONST char *xf86FindOptionValue(OPTTYPE options, const char *name);
+extern int xf86NameCmp(const char *s1, const char *s2);
+extern char *xf86CheckStrOption(OPTTYPE optlist, const char *name, char *deflt);
+
+extern char *xf86SetStrOption(OPTTYPE optlist, const char *name,
+ CONST char *deflt);
+extern int xf86SetBoolOption(OPTTYPE optlist, const char *name, int deflt);
+extern OPTTYPE xf86AddNewOption(OPTTYPE head, const char *name,
+ const char *val);
+extern CONST char *xf86FindOptionValue(OPTTYPE options, const char *name);
+extern char *xf86OptionName(OPTTYPE opt);
+extern char *xf86OptionValue(OPTTYPE opt);
+extern int xf86NameCmp(const char *s1, const char *s2);
+extern char *xf86CheckStrOption(OPTTYPE optlist, const char *name, char *deflt);
+extern void xf86AddEnabledDevice(InputInfoPtr pInfo);
+extern void xf86RemoveEnabledDevice(InputInfoPtr pInfo);
+extern Atom XIGetKnownProperty(char *name);
+extern void xf86AddInputDriver(InputDriverPtr driver, pointer module,
+ int flags);
+extern int
+ xf86ScaleAxis(int Cx, int to_max, int to_min, int from_max, int from_min);
+
+extern void DeleteInputDeviceRequest(DeviceIntPtr pDev);
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+extern void FreeInputAttributes(InputAttributes * attrs);
+#endif
+extern void
+
+xf86PostButtonEvent(DeviceIntPtr device,
+ int is_absolute,
+ int button,
+ int is_down, int first_valuator, int num_valuators, ...);
+extern int Xasprintf(char **ret, const char *format, ...);
+extern int
+ XISetDevicePropertyDeletable(DeviceIntPtr dev, Atom property, Bool deletable);
+
+extern InputInfoPtr xf86FirstLocalDevice(void);
+extern void xf86DeleteInput(InputInfoPtr pInp, int flags);
+extern OPTTYPE xf86OptionListDuplicate(OPTTYPE options);
+extern Bool
+
+InitButtonClassDeviceStruct(DeviceIntPtr dev, int numButtons, Atom *labels,
+ CARD8 *map);
+extern void
+
+InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
+ int maxval, int resolution, int min_res, int max_res,
+ int mode);
+extern void
+ xf86PostKeyboardEvent(DeviceIntPtr device, unsigned int key_code, int is_down);
+extern int
+ xf86SetIntOption(OPTTYPE optlist, const char *name, int deflt);
+extern void
+
+xf86PostButtonEventP(DeviceIntPtr device,
+ int is_absolute,
+ int button,
+ int is_down, int first_valuator, int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators);
+extern Bool
+ InitPtrFeedbackClassDeviceStruct(DeviceIntPtr dev, PtrCtrlProcPtr controlProc);
+
+extern int
+
+XIChangeDeviceProperty(DeviceIntPtr dev, Atom property, Atom type,
+ int format, int mode, unsigned long len,
+ OPTTYPE value, Bool sendevent);
+extern CARD32 GetTimeInMillis(void);
+
+extern int
+ NewInputDeviceRequest(InputOption *options,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+ InputAttributes * attrs,
+#endif
+ DeviceIntPtr *pdev);
+
+extern Bool
+ InitLedFeedbackClassDeviceStruct(DeviceIntPtr dev, LedCtrlProcPtr controlProc);
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+extern InputAttributes *DuplicateInputAttributes(InputAttributes * attrs);
+#endif
+extern int ValidAtom(Atom atom);
+extern Bool
+
+InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet * rmlvo,
+ BellProcPtr bell_func, KbdCtrlProcPtr ctrl_func);
+extern long
+
+XIRegisterPropertyHandler(DeviceIntPtr dev,
+ int (*SetProperty) (DeviceIntPtr dev,
+ Atom property,
+ XIPropertyValuePtr prop,
+ BOOL checkonly),
+ int (*GetProperty) (DeviceIntPtr dev,
+ Atom property),
+ int (*DeleteProperty) (DeviceIntPtr dev,
+ Atom property));
+extern int InitProximityClassDeviceStruct(DeviceIntPtr dev);
+extern void xf86Msg(MessageType type, const char *format, ...);
+extern void xf86MsgVerb(MessageType type, int verb, const char *format, ...);
+extern void xf86IDrvMsg(InputInfoPtr dev, MessageType type, const char *format,
+ ...);
+
+extern void
+
+xf86PostMotionEventP(DeviceIntPtr device,
+ int is_absolute, int first_valuator, int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators);
+
+extern Bool
+
+InitValuatorClassDeviceStruct(DeviceIntPtr dev, int numAxes, Atom *labels,
+ int numMotionEvents, int mode);
+
+extern OPTTYPE
+xf86ReplaceStrOption(OPTTYPE optlist, const char *name, const char *val);
+
+extern OPTTYPE xf86NextOption(OPTTYPE list);
+
+extern int
+
+XIGetDeviceProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr *value);
+
+extern Atom MakeAtom(const char *string, unsigned len, Bool makeit);
+
+extern int GetMotionHistorySize(void);
+
+extern void
+
+xf86PostProximityEventP(DeviceIntPtr device,
+ int is_in, int first_valuator, int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators);
+
+extern Bool InitFocusClassDeviceStruct(DeviceIntPtr dev);
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12
+extern void
+ xf86ProcessCommonOptions(InputInfoPtr pInfo, OPTTYPE list);
+
+extern void
+
+xf86CollectInputOptions(InputInfoPtr pInfo,
+ const char **defaultOpts, OPTTYPE extraOpts);
+
+extern InputInfoPtr xf86AllocateInput(InputDriverPtr drv, int flags);
+
+extern ClientPtr serverClient;
+
+extern Bool
+QueueWorkProc(Bool (*function)
+ (ClientPtr /* pClient */ , pointer /* closure */ ),
+ ClientPtr client, pointer closure);
+#endif
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644
index 0000000..f3b80fc
--- /dev/null
+++ b/tools/.gitignore
@@ -0,0 +1,3 @@
+# Add & Override for this directory and it's subdirectories
+synclient
+syndaemon
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 0000000..e790905
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,31 @@
+# Copyright 2008 Red Hat, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# 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.
+
+bin_PROGRAMS = synclient syndaemon
+
+AM_CPPFLAGS = -I$(top_srcdir)/include
+AM_CFLAGS = $(XI_CFLAGS)
+AM_LDFLAGS = $(XI_LIBS)
+
+synclient_SOURCES = synclient.c
+
+syndaemon_SOURCES = syndaemon.c
+syndaemon_CFLAGS = $(AM_CFLAGS) $(XTST_CFLAGS)
+syndaemon_LDFLAGS = $(AM_LDFLAGS) $(XTST_LIBS)
diff --git a/tools/synclient.c b/tools/synclient.c
new file mode 100644
index 0000000..f08a275
--- /dev/null
+++ b/tools/synclient.c
@@ -0,0 +1,645 @@
+/*
+ * Copyright © 2002-2005,2007 Peter Osterlund
+ *
+ * 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 Osterlund (petero2@telia.com)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <string.h>
+#include <stddef.h>
+#include <math.h>
+#include <limits.h>
+
+#include <X11/Xdefs.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/XI.h>
+#include <X11/extensions/XInput.h>
+#include "synaptics.h"
+#include "synaptics-properties.h"
+
+#ifndef XATOM_FLOAT
+#define XATOM_FLOAT "FLOAT"
+#endif
+
+union flong { /* Xlibs 64-bit property handling madness */
+ long l;
+ float f;
+};
+
+enum ParaType {
+ PT_INT,
+ PT_BOOL,
+ PT_DOUBLE
+};
+
+struct Parameter {
+ char *name; /* Name of parameter */
+ enum ParaType type; /* Type of parameter */
+ double min_val; /* Minimum allowed value */
+ double max_val; /* Maximum allowed value */
+ char *prop_name; /* Property name */
+ int prop_format; /* Property format (0 for floats) */
+ int prop_offset; /* Offset inside property */
+};
+
+static struct Parameter params[] = {
+ {"LeftEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 0},
+ {"RightEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 1},
+ {"TopEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 2},
+ {"BottomEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 3},
+ {"FingerLow", PT_INT, 0, 255, SYNAPTICS_PROP_FINGER, 32, 0},
+ {"FingerHigh", PT_INT, 0, 255, SYNAPTICS_PROP_FINGER, 32, 1},
+ {"FingerPress", PT_INT, 0, 256, SYNAPTICS_PROP_FINGER, 32, 2},
+ {"MaxTapTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_TIME, 32, 0},
+ {"MaxTapMove", PT_INT, 0, 2000, SYNAPTICS_PROP_TAP_MOVE, 32, 0},
+ {"MaxDoubleTapTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS,32, 1},
+ {"SingleTapTimeout", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS,32, 0},
+ {"ClickTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS,32, 2},
+ {"FastTaps", PT_BOOL, 0, 1, SYNAPTICS_PROP_TAP_FAST, 8, 0},
+ {"EmulateMidButtonTime", PT_INT, 0, 1000, SYNAPTICS_PROP_MIDDLE_TIMEOUT,32, 0},
+ {"EmulateTwoFingerMinZ", PT_INT, 0, 1000, SYNAPTICS_PROP_TWOFINGER_PRESSURE, 32, 0},
+ {"EmulateTwoFingerMinW", PT_INT, 0, 15, SYNAPTICS_PROP_TWOFINGER_WIDTH, 32, 0},
+ {"VertScrollDelta", PT_INT, -1000, 1000, SYNAPTICS_PROP_SCROLL_DISTANCE, 32, 0},
+ {"HorizScrollDelta", PT_INT, -1000, 1000, SYNAPTICS_PROP_SCROLL_DISTANCE, 32, 1},
+ {"VertEdgeScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 0},
+ {"HorizEdgeScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 1},
+ {"CornerCoasting", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 2},
+ {"VertTwoFingerScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_TWOFINGER, 8, 0},
+ {"HorizTwoFingerScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_TWOFINGER, 8, 1},
+ {"MinSpeed", PT_DOUBLE, 0, 255.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 0},
+ {"MaxSpeed", PT_DOUBLE, 0, 255.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 1},
+ {"AccelFactor", PT_DOUBLE, 0, 1.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 2},
+ {"TrackstickSpeed", PT_DOUBLE, 0, 200.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 3},
+ {"EdgeMotionMinZ", PT_INT, 1, 255, SYNAPTICS_PROP_EDGEMOTION_PRESSURE, 32, 0},
+ {"EdgeMotionMaxZ", PT_INT, 1, 255, SYNAPTICS_PROP_EDGEMOTION_PRESSURE, 32, 1},
+ {"EdgeMotionMinSpeed", PT_INT, 0, 1000, SYNAPTICS_PROP_EDGEMOTION_SPEED, 32, 0},
+ {"EdgeMotionMaxSpeed", PT_INT, 0, 1000, SYNAPTICS_PROP_EDGEMOTION_SPEED, 32, 1},
+ {"EdgeMotionUseAlways", PT_BOOL, 0, 1, SYNAPTICS_PROP_EDGEMOTION, 8, 0},
+ {"UpDownScrolling", PT_BOOL, 0, 1, SYNAPTICS_PROP_BUTTONSCROLLING, 8, 0},
+ {"LeftRightScrolling", PT_BOOL, 0, 1, SYNAPTICS_PROP_BUTTONSCROLLING, 8, 1},
+ {"UpDownScrollRepeat", PT_BOOL, 0, 1, SYNAPTICS_PROP_BUTTONSCROLLING_REPEAT, 8, 0},
+ {"LeftRightScrollRepeat", PT_BOOL, 0, 1, SYNAPTICS_PROP_BUTTONSCROLLING_REPEAT, 8, 1},
+ {"ScrollButtonRepeat", PT_INT, SBR_MIN , SBR_MAX, SYNAPTICS_PROP_BUTTONSCROLLING_TIME, 32, 0},
+ {"TouchpadOff", PT_INT, 0, 2, SYNAPTICS_PROP_OFF, 8, 0},
+ {"LockedDrags", PT_BOOL, 0, 1, SYNAPTICS_PROP_LOCKED_DRAGS, 8, 0},
+ {"LockedDragTimeout", PT_INT, 0, 30000, SYNAPTICS_PROP_LOCKED_DRAGS_TIMEOUT, 32, 0},
+ {"RTCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 0},
+ {"RBCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 1},
+ {"LTCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 2},
+ {"LBCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 3},
+ {"TapButton1", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 4},
+ {"TapButton2", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 5},
+ {"TapButton3", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 6},
+ {"ClickFinger1", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 0},
+ {"ClickFinger2", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 1},
+ {"ClickFinger3", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 2},
+ {"CircularScrolling", PT_BOOL, 0, 1, SYNAPTICS_PROP_CIRCULAR_SCROLLING, 8, 0},
+ {"CircScrollDelta", PT_DOUBLE, .01, 3, SYNAPTICS_PROP_CIRCULAR_SCROLLING_DIST, 0 /* float */, 0},
+ {"CircScrollTrigger", PT_INT, 0, 8, SYNAPTICS_PROP_CIRCULAR_SCROLLING_TRIGGER, 8, 0},
+ {"CircularPad", PT_BOOL, 0, 1, SYNAPTICS_PROP_CIRCULAR_PAD, 8, 0},
+ {"PalmDetect", PT_BOOL, 0, 1, SYNAPTICS_PROP_PALM_DETECT, 8, 0},
+ {"PalmMinWidth", PT_INT, 0, 15, SYNAPTICS_PROP_PALM_DIMENSIONS, 32, 0},
+ {"PalmMinZ", PT_INT, 0, 255, SYNAPTICS_PROP_PALM_DIMENSIONS, 32, 1},
+ {"CoastingSpeed", PT_DOUBLE, 0, 255, SYNAPTICS_PROP_COASTING_SPEED, 0 /* float*/, 0},
+ {"CoastingFriction", PT_DOUBLE, 0, 255, SYNAPTICS_PROP_COASTING_SPEED, 0 /* float*/, 1},
+ {"PressureMotionMinZ", PT_INT, 1, 255, SYNAPTICS_PROP_PRESSURE_MOTION, 32, 0},
+ {"PressureMotionMaxZ", PT_INT, 1, 255, SYNAPTICS_PROP_PRESSURE_MOTION, 32, 1},
+ {"PressureMotionMinFactor", PT_DOUBLE, 0, 10.0,SYNAPTICS_PROP_PRESSURE_MOTION_FACTOR, 0 /*float*/, 0},
+ {"PressureMotionMaxFactor", PT_DOUBLE, 0, 10.0,SYNAPTICS_PROP_PRESSURE_MOTION_FACTOR, 0 /*float*/, 1},
+ {"GrabEventDevice", PT_BOOL, 0, 1, SYNAPTICS_PROP_GRAB, 8, 0},
+ {"TapAndDragGesture", PT_BOOL, 0, 1, SYNAPTICS_PROP_GESTURES, 8, 0},
+ {"AreaLeftEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 0},
+ {"AreaRightEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 1},
+ {"AreaTopEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 2},
+ {"AreaBottomEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 3},
+ {"HorizHysteresis", PT_INT, 0, 10000, SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 0},
+ {"VertHysteresis", PT_INT, 0, 10000, SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 1},
+ {"ClickPad", PT_BOOL, 0, 1, SYNAPTICS_PROP_CLICKPAD, 8, 0},
+ {"RightButtonAreaLeft", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 0},
+ {"RightButtonAreaRight", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 1},
+ {"RightButtonAreaTop", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 2},
+ {"RightButtonAreaBottom", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 3},
+ {"MiddleButtonAreaLeft", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 4},
+ {"MiddleButtonAreaRight", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 5},
+ {"MiddleButtonAreaTop", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 6},
+ {"MiddleButtonAreaBottom", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 7},
+ { NULL, 0, 0, 0, 0 }
+};
+
+static double
+parse_cmd(char *cmd, struct Parameter **par)
+{
+ char *eqp = strchr(cmd, '=');
+
+ *par = NULL;
+
+ if (eqp) {
+ int j;
+ int found = 0;
+
+ *eqp = 0;
+ for (j = 0; params[j].name; j++) {
+ if (strcasecmp(cmd, params[j].name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ double val = atof(&eqp[1]);
+
+ *par = &params[j];
+
+ if (val < (*par)->min_val)
+ val = (*par)->min_val;
+ if (val > (*par)->max_val)
+ val = (*par)->max_val;
+
+ return val;
+ }
+ else {
+ printf("Unknown parameter %s\n", cmd);
+ }
+ }
+ else {
+ printf("Invalid command: %s\n", cmd);
+ }
+
+ return 0;
+}
+
+static int
+is_equal(SynapticsSHM * s1, SynapticsSHM * s2)
+{
+ int i;
+
+ if ((s1->x != s2->x) ||
+ (s1->y != s2->y) ||
+ (s1->z != s2->z) ||
+ (s1->numFingers != s2->numFingers) ||
+ (s1->fingerWidth != s2->fingerWidth) ||
+ (s1->left != s2->left) ||
+ (s1->right != s2->right) ||
+ (s1->up != s2->up) ||
+ (s1->down != s2->down) || (s1->middle != s2->middle))
+ return 0;
+
+ for (i = 0; i < 8; i++)
+ if (s1->multi[i] != s2->multi[i])
+ return 0;
+
+ return 1;
+}
+
+static double
+get_time(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec + tv.tv_usec / 1000000.0;
+}
+
+static void
+shm_monitor(SynapticsSHM * synshm, int delay)
+{
+ int header = 0;
+ SynapticsSHM old;
+ double t0 = get_time();
+
+ memset(&old, 0, sizeof(SynapticsSHM));
+ old.x = -1; /* Force first equality test to fail */
+
+ while (1) {
+ SynapticsSHM cur = *synshm;
+
+ if (!is_equal(&old, &cur)) {
+ if (!header) {
+ printf("%8s %4s %4s %3s %s %2s %2s %s %s %s %s %8s "
+ "%2s %2s %2s %3s %3s\n",
+ "time", "x", "y", "z", "f", "w", "l", "r", "u", "d", "m",
+ "multi", "gl", "gm", "gr", "gdx", "gdy");
+ header = 20;
+ }
+ header--;
+ printf
+ ("%8.3f %4d %4d %3d %d %2d %2d %d %d %d %d %d%d%d%d%d%d%d%d\n",
+ get_time() - t0, cur.x, cur.y, cur.z, cur.numFingers,
+ cur.fingerWidth, cur.left, cur.right, cur.up, cur.down,
+ cur.middle, cur.multi[0], cur.multi[1], cur.multi[2],
+ cur.multi[3], cur.multi[4], cur.multi[5], cur.multi[6],
+ cur.multi[7]);
+ fflush(stdout);
+ old = cur;
+ }
+ usleep(delay * 1000);
+ }
+}
+
+/** Init and return SHM area or NULL on error */
+static SynapticsSHM *
+shm_init()
+{
+ SynapticsSHM *synshm = NULL;
+ int shmid = 0;
+
+ if ((shmid = shmget(SHM_SYNAPTICS, sizeof(SynapticsSHM), 0)) == -1) {
+ if ((shmid = shmget(SHM_SYNAPTICS, 0, 0)) == -1)
+ fprintf(stderr,
+ "Can't access shared memory area. SHMConfig disabled?\n");
+ else
+ fprintf(stderr,
+ "Incorrect size of shared memory area. Incompatible driver version?\n");
+ }
+ else if ((synshm = (SynapticsSHM *) shmat(shmid, NULL, SHM_RDONLY)) == NULL)
+ perror("shmat");
+
+ return synshm;
+}
+
+static void
+shm_process_commands(int do_monitor, int delay)
+{
+ SynapticsSHM *synshm = NULL;
+
+ synshm = shm_init();
+ if (!synshm)
+ return;
+
+ if (do_monitor)
+ shm_monitor(synshm, delay);
+}
+
+/** Init display connection or NULL on error */
+static Display *
+dp_init()
+{
+ Display *dpy = NULL;
+ XExtensionVersion *v = NULL;
+ Atom touchpad_type = 0;
+ Atom synaptics_property = 0;
+ int error = 0;
+
+ dpy = XOpenDisplay(NULL);
+ if (!dpy) {
+ fprintf(stderr, "Failed to connect to X Server.\n");
+ error = 1;
+ goto unwind;
+ }
+
+ v = XGetExtensionVersion(dpy, INAME);
+ if (!v->present ||
+ (v->major_version * 1000 + v->minor_version) <
+ (XI_Add_DeviceProperties_Major * 1000 +
+ XI_Add_DeviceProperties_Minor)) {
+ fprintf(stderr, "X server supports X Input %d.%d. I need %d.%d.\n",
+ v->major_version, v->minor_version,
+ XI_Add_DeviceProperties_Major, XI_Add_DeviceProperties_Minor);
+ error = 1;
+ goto unwind;
+ }
+
+ /* We know synaptics sets XI_TOUCHPAD for all the devices. */
+ touchpad_type = XInternAtom(dpy, XI_TOUCHPAD, True);
+ if (!touchpad_type) {
+ fprintf(stderr, "XI_TOUCHPAD not initialised.\n");
+ error = 1;
+ goto unwind;
+ }
+
+ synaptics_property = XInternAtom(dpy, SYNAPTICS_PROP_EDGES, True);
+ if (!synaptics_property) {
+ fprintf(stderr, "Couldn't find synaptics properties. No synaptics "
+ "driver loaded?\n");
+ error = 1;
+ goto unwind;
+ }
+
+ unwind:
+ XFree(v);
+ if (error && dpy) {
+ XCloseDisplay(dpy);
+ dpy = NULL;
+ }
+ return dpy;
+}
+
+static XDevice *
+dp_get_device(Display * dpy)
+{
+ XDevice *dev = NULL;
+ XDeviceInfo *info = NULL;
+ int ndevices = 0;
+ Atom touchpad_type = 0;
+ Atom synaptics_property = 0;
+ Atom *properties = NULL;
+ int nprops = 0;
+ int error = 0;
+
+ touchpad_type = XInternAtom(dpy, XI_TOUCHPAD, True);
+ synaptics_property = XInternAtom(dpy, SYNAPTICS_PROP_EDGES, True);
+ info = XListInputDevices(dpy, &ndevices);
+
+ while (ndevices--) {
+ if (info[ndevices].type == touchpad_type) {
+ dev = XOpenDevice(dpy, info[ndevices].id);
+ if (!dev) {
+ fprintf(stderr, "Failed to open device '%s'.\n",
+ info[ndevices].name);
+ error = 1;
+ goto unwind;
+ }
+
+ properties = XListDeviceProperties(dpy, dev, &nprops);
+ if (!properties || !nprops) {
+ fprintf(stderr, "No properties on device '%s'.\n",
+ info[ndevices].name);
+ error = 1;
+ goto unwind;
+ }
+
+ while (nprops--) {
+ if (properties[nprops] == synaptics_property)
+ break;
+ }
+ if (!nprops) {
+ fprintf(stderr, "No synaptics properties on device '%s'.\n",
+ info[ndevices].name);
+ error = 1;
+ goto unwind;
+ }
+
+ break; /* Yay, device is suitable */
+ }
+ }
+
+ unwind:
+ XFree(properties);
+ XFreeDeviceList(info);
+ if (!dev)
+ fprintf(stderr, "Unable to find a synaptics device.\n");
+ else if (error && dev) {
+ XCloseDevice(dpy, dev);
+ dev = NULL;
+ }
+ return dev;
+}
+
+static void
+dp_set_variables(Display * dpy, XDevice * dev, int argc, char *argv[],
+ int first_cmd)
+{
+ int i;
+ double val;
+ struct Parameter *par;
+ Atom prop, type, float_type;
+ int format;
+ unsigned char *data;
+ unsigned long nitems, bytes_after;
+
+ union flong *f;
+ long *n;
+ char *b;
+
+ float_type = XInternAtom(dpy, XATOM_FLOAT, True);
+ if (!float_type)
+ fprintf(stderr, "Float properties not available.\n");
+
+ for (i = first_cmd; i < argc; i++) {
+ val = parse_cmd(argv[i], &par);
+ if (!par)
+ continue;
+
+ prop = XInternAtom(dpy, par->prop_name, True);
+ if (!prop) {
+ fprintf(stderr, "Property for '%s' not available. Skipping.\n",
+ par->name);
+ continue;
+
+ }
+
+ XGetDeviceProperty(dpy, dev, prop, 0, 1000, False, AnyPropertyType,
+ &type, &format, &nitems, &bytes_after, &data);
+
+ if (type == None) {
+ fprintf(stderr, "Property for '%s' not available. Skipping.\n",
+ par->name);
+ continue;
+ }
+
+ switch (par->prop_format) {
+ case 8:
+ if (format != par->prop_format || type != XA_INTEGER) {
+ fprintf(stderr, " %-23s = format mismatch (%d)\n",
+ par->name, format);
+ break;
+ }
+ b = (char *) data;
+ b[par->prop_offset] = rint(val);
+ break;
+ case 32:
+ if (format != par->prop_format ||
+ (type != XA_INTEGER && type != XA_CARDINAL)) {
+ fprintf(stderr, " %-23s = format mismatch (%d)\n",
+ par->name, format);
+ break;
+ }
+ n = (long *) data;
+ n[par->prop_offset] = rint(val);
+ break;
+ case 0: /* float */
+ if (!float_type)
+ continue;
+ if (format != 32 || type != float_type) {
+ fprintf(stderr, " %-23s = format mismatch (%d)\n",
+ par->name, format);
+ break;
+ }
+ f = (union flong *) data;
+ f[par->prop_offset].f = val;
+ break;
+ }
+
+ XChangeDeviceProperty(dpy, dev, prop, type, format,
+ PropModeReplace, data, nitems);
+ XFlush(dpy);
+ }
+}
+
+/* FIXME: horribly inefficient. */
+static void
+dp_show_settings(Display * dpy, XDevice * dev)
+{
+ int j;
+ Atom a, type, float_type;
+ int format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+ int len;
+
+ union flong *f;
+ long *i;
+ char *b;
+
+ float_type = XInternAtom(dpy, XATOM_FLOAT, True);
+ if (!float_type)
+ fprintf(stderr, "Float properties not available.\n");
+
+ printf("Parameter settings:\n");
+ for (j = 0; params[j].name; j++) {
+ struct Parameter *par = &params[j];
+
+ a = XInternAtom(dpy, par->prop_name, True);
+ if (!a)
+ continue;
+
+ len =
+ 1 +
+ ((par->prop_offset * (par->prop_format ? par->prop_format : 32) /
+ 8)) / 4;
+
+ XGetDeviceProperty(dpy, dev, a, 0, len, False,
+ AnyPropertyType, &type, &format,
+ &nitems, &bytes_after, &data);
+ if (type == None)
+ continue;
+
+ switch (par->prop_format) {
+ case 8:
+ if (format != par->prop_format || type != XA_INTEGER) {
+ fprintf(stderr, " %-23s = format mismatch (%d)\n",
+ par->name, format);
+ break;
+ }
+
+ b = (char *) data;
+ printf(" %-23s = %d\n", par->name, b[par->prop_offset]);
+ break;
+ case 32:
+ if (format != par->prop_format ||
+ (type != XA_INTEGER && type != XA_CARDINAL)) {
+ fprintf(stderr, " %-23s = format mismatch (%d)\n",
+ par->name, format);
+ break;
+ }
+
+ i = (long *) data;
+ printf(" %-23s = %ld\n", par->name, i[par->prop_offset]);
+ break;
+ case 0: /* Float */
+ if (!float_type)
+ continue;
+ if (format != 32 || type != float_type) {
+ fprintf(stderr, " %-23s = format mismatch (%d)\n",
+ par->name, format);
+ break;
+ }
+
+ f = (union flong *) data;
+ printf(" %-23s = %g\n", par->name, f[par->prop_offset].f);
+ break;
+ }
+
+ XFree(data);
+ }
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "Usage: synclient [-s] [-m interval] [-h] [-l] [-V] [-?] [var1=value1 [var2=value2] ...]\n");
+ fprintf(stderr,
+ " -m monitor changes to the touchpad state (implies -s)\n"
+ " interval specifies how often (in ms) to poll the touchpad state\n");
+ fprintf(stderr, " -l List current user settings\n");
+ fprintf(stderr, " -V Print synclient version string and exit\n");
+ fprintf(stderr, " -? Show this help message\n");
+ fprintf(stderr, " var=value Set user parameter 'var' to 'value'.\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ int delay = -1;
+ int do_monitor = 0;
+ int dump_settings = 0;
+ int first_cmd;
+
+ Display *dpy;
+ XDevice *dev;
+
+ if (argc == 1)
+ dump_settings = 1;
+
+ /* Parse command line parameters */
+ while ((c = getopt(argc, argv, "sm:hlV")) != -1) {
+ switch (c) {
+ case 'm':
+ do_monitor = 1;
+ if ((delay = atoi(optarg)) < 0)
+ usage();
+ break;
+ case 'l':
+ dump_settings = 1;
+ break;
+ case 'V':
+ printf("%s\n", VERSION);
+ exit(0);
+ default:
+ usage();
+ }
+ }
+
+ first_cmd = optind;
+ if (!do_monitor && !dump_settings && first_cmd == argc)
+ usage();
+
+ /* Connect to the shared memory area */
+ if (do_monitor)
+ shm_process_commands(do_monitor, delay);
+
+ dpy = dp_init();
+ if (!dpy || !(dev = dp_get_device(dpy)))
+ return 1;
+
+ dp_set_variables(dpy, dev, argc, argv, first_cmd);
+ if (dump_settings)
+ dp_show_settings(dpy, dev);
+
+ XCloseDevice(dpy, dev);
+ XCloseDisplay(dpy);
+
+ return 0;
+}
diff --git a/tools/syndaemon.c b/tools/syndaemon.c
new file mode 100644
index 0000000..5109052
--- /dev/null
+++ b/tools/syndaemon.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright © 2003-2004 Peter Osterlund
+ *
+ * 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 Osterlund (petero2@telia.com)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/XInput.h>
+#ifdef HAVE_X11_EXTENSIONS_RECORD_H
+#include <X11/Xproto.h>
+#include <X11/extensions/record.h>
+#endif /* HAVE_X11_EXTENSIONS_RECORD_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include "synaptics.h"
+#include "synaptics-properties.h"
+
+typedef enum {
+ TouchpadOn = 0,
+ TouchpadOff = 1,
+ TappingOff = 2
+} TouchpadState;
+
+static Bool pad_disabled
+ /* internal flag, this does not correspond to device state */ ;
+static int ignore_modifier_combos;
+static int ignore_modifier_keys;
+static int background;
+static const char *pid_file;
+static Display *display;
+static XDevice *dev;
+static Atom touchpad_off_prop;
+static TouchpadState previous_state;
+static TouchpadState disable_state = TouchpadOff;
+static int verbose;
+
+#define KEYMAP_SIZE 32
+static unsigned char keyboard_mask[KEYMAP_SIZE];
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "Usage: syndaemon [-i idle-time] [-m poll-delay] [-d] [-t] [-k]\n");
+ fprintf(stderr,
+ " -i How many seconds to wait after the last key press before\n");
+ fprintf(stderr, " enabling the touchpad. (default is 2.0s)\n");
+ fprintf(stderr, " -m How many milli-seconds to wait until next poll.\n");
+ fprintf(stderr, " (default is 200ms)\n");
+ fprintf(stderr, " -d Start as a daemon, i.e. in the background.\n");
+ fprintf(stderr, " -p Create a pid file with the specified name.\n");
+ fprintf(stderr,
+ " -t Only disable tapping and scrolling, not mouse movements.\n");
+ fprintf(stderr,
+ " -k Ignore modifier keys when monitoring keyboard activity.\n");
+ fprintf(stderr, " -K Like -k but also ignore Modifier+Key combos.\n");
+ fprintf(stderr, " -R Use the XRecord extension.\n");
+ fprintf(stderr, " -v Print diagnostic messages.\n");
+ exit(1);
+}
+
+static void
+store_current_touchpad_state(void)
+{
+ Atom real_type;
+ int real_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+
+ if ((XGetDeviceProperty(display, dev, touchpad_off_prop, 0, 1, False,
+ XA_INTEGER, &real_type, &real_format, &nitems,
+ &bytes_after, &data) == Success) &&
+ (real_type != None)) {
+ previous_state = data[0];
+ }
+}
+
+/**
+ * Toggle touchpad enabled/disabled state, decided by value.
+ */
+static void
+toggle_touchpad(Bool enable)
+{
+ unsigned char data;
+
+ if (pad_disabled && enable) {
+ data = previous_state;
+ pad_disabled = False;
+ if (verbose)
+ printf("Enable\n");
+ }
+ else if (!pad_disabled && !enable &&
+ previous_state != disable_state && previous_state != TouchpadOff) {
+ store_current_touchpad_state();
+ pad_disabled = True;
+ data = disable_state;
+ if (verbose)
+ printf("Disable\n");
+ }
+ else
+ return;
+
+ /* This potentially overwrites a different client's setting, but ... */
+ XChangeDeviceProperty(display, dev, touchpad_off_prop, XA_INTEGER, 8,
+ PropModeReplace, &data, 1);
+ XFlush(display);
+}
+
+static void
+signal_handler(int signum)
+{
+ toggle_touchpad(True);
+
+ if (pid_file)
+ unlink(pid_file);
+ kill(getpid(), signum);
+}
+
+static void
+install_signal_handler(void)
+{
+ static int signals[] = {
+ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT,
+ SIGBUS, SIGFPE, SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE,
+ SIGALRM, SIGTERM,
+#ifdef SIGPWR
+ SIGPWR
+#endif
+ };
+ int i;
+ struct sigaction act;
+ sigset_t set;
+
+ sigemptyset(&set);
+ act.sa_handler = signal_handler;
+ act.sa_mask = set;
+#ifdef SA_ONESHOT
+ act.sa_flags = SA_ONESHOT;
+#else
+ act.sa_flags = 0;
+#endif
+
+ for (i = 0; i < sizeof(signals) / sizeof(int); i++) {
+ if (sigaction(signals[i], &act, NULL) == -1) {
+ perror("sigaction");
+ exit(2);
+ }
+ }
+}
+
+/**
+ * Return non-zero if the keyboard state has changed since the last call.
+ */
+static int
+keyboard_activity(Display * display)
+{
+ static unsigned char old_key_state[KEYMAP_SIZE];
+ unsigned char key_state[KEYMAP_SIZE];
+ int i;
+ int ret = 0;
+
+ XQueryKeymap(display, (char *) key_state);
+
+ for (i = 0; i < KEYMAP_SIZE; i++) {
+ if ((key_state[i] & ~old_key_state[i]) & keyboard_mask[i]) {
+ ret = 1;
+ break;
+ }
+ }
+ if (ignore_modifier_combos) {
+ for (i = 0; i < KEYMAP_SIZE; i++) {
+ if (key_state[i] & ~keyboard_mask[i]) {
+ ret = 0;
+ break;
+ }
+ }
+ }
+ for (i = 0; i < KEYMAP_SIZE; i++)
+ old_key_state[i] = key_state[i];
+ return ret;
+}
+
+static double
+get_time(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec + tv.tv_usec / 1000000.0;
+}
+
+static void
+main_loop(Display * display, double idle_time, int poll_delay)
+{
+ double last_activity = 0.0;
+ double current_time;
+
+ keyboard_activity(display);
+
+ for (;;) {
+ current_time = get_time();
+ if (keyboard_activity(display))
+ last_activity = current_time;
+
+ /* If system times goes backwards, touchpad can get locked. Make
+ * sure our last activity wasn't in the future and reset if it was. */
+ if (last_activity > current_time)
+ last_activity = current_time - idle_time - 1;
+
+ if (current_time > last_activity + idle_time) { /* Enable touchpad */
+ toggle_touchpad(True);
+ }
+ else { /* Disable touchpad */
+ toggle_touchpad(False);
+ }
+
+ usleep(poll_delay);
+ }
+}
+
+static void
+clear_bit(unsigned char *ptr, int bit)
+{
+ int byte_num = bit / 8;
+ int bit_num = bit % 8;
+
+ ptr[byte_num] &= ~(1 << bit_num);
+}
+
+static void
+setup_keyboard_mask(Display * display, int ignore_modifier_keys)
+{
+ XModifierKeymap *modifiers;
+ int i;
+
+ for (i = 0; i < KEYMAP_SIZE; i++)
+ keyboard_mask[i] = 0xff;
+
+ if (ignore_modifier_keys) {
+ modifiers = XGetModifierMapping(display);
+ for (i = 0; i < 8 * modifiers->max_keypermod; i++) {
+ KeyCode kc = modifiers->modifiermap[i];
+
+ if (kc != 0)
+ clear_bit(keyboard_mask, kc);
+ }
+ XFreeModifiermap(modifiers);
+ }
+}
+
+/* ---- the following code is for using the xrecord extension ----- */
+#ifdef HAVE_X11_EXTENSIONS_RECORD_H
+
+#define MAX_MODIFIERS 16
+
+/* used for exchanging information with the callback function */
+struct xrecord_callback_results {
+ XModifierKeymap *modifiers;
+ Bool key_event;
+ Bool non_modifier_event;
+ KeyCode pressed_modifiers[MAX_MODIFIERS];
+};
+
+/* test if the xrecord extension is found */
+Bool
+check_xrecord(Display * display)
+{
+
+ Bool found;
+ Status status;
+ int major_opcode, minor_opcode, first_error;
+ int version[2];
+
+ found = XQueryExtension(display,
+ "RECORD",
+ &major_opcode, &minor_opcode, &first_error);
+
+ status = XRecordQueryVersion(display, version, version + 1);
+ if (verbose && status) {
+ printf("X RECORD extension version %d.%d\n", version[0], version[1]);
+ }
+ return found;
+}
+
+/* called by XRecordProcessReplies() */
+void
+xrecord_callback(XPointer closure, XRecordInterceptData * recorded_data)
+{
+
+ struct xrecord_callback_results *cbres;
+ xEvent *xev;
+ int nxev;
+
+ cbres = (struct xrecord_callback_results *) closure;
+
+ if (recorded_data->category != XRecordFromServer) {
+ XRecordFreeData(recorded_data);
+ return;
+ }
+
+ nxev = recorded_data->data_len / 8;
+ xev = (xEvent *) recorded_data->data;
+ while (nxev--) {
+
+ if ((xev->u.u.type == KeyPress) || (xev->u.u.type == KeyRelease)) {
+ int i;
+ int is_modifier = 0;
+
+ cbres->key_event = 1; /* remember, a key was pressed or released. */
+
+ /* test if it was a modifier */
+ for (i = 0; i < 8 * cbres->modifiers->max_keypermod; i++) {
+ KeyCode kc = cbres->modifiers->modifiermap[i];
+
+ if (kc == xev->u.u.detail) {
+ is_modifier = 1; /* yes, it is a modifier. */
+ break;
+ }
+ }
+
+ if (is_modifier) {
+ if (xev->u.u.type == KeyPress) {
+ for (i = 0; i < MAX_MODIFIERS; ++i)
+ if (!cbres->pressed_modifiers[i]) {
+ cbres->pressed_modifiers[i] = xev->u.u.detail;
+ break;
+ }
+ }
+ else { /* KeyRelease */
+ for (i = 0; i < MAX_MODIFIERS; ++i)
+ if (cbres->pressed_modifiers[i] == xev->u.u.detail)
+ cbres->pressed_modifiers[i] = 0;
+ }
+
+ }
+ else {
+ /* remember, a non-modifier was pressed. */
+ cbres->non_modifier_event = 1;
+ }
+ }
+
+ xev++;
+ }
+
+ XRecordFreeData(recorded_data); /* cleanup */
+}
+
+static int
+is_modifier_pressed(const struct xrecord_callback_results *cbres)
+{
+ int i;
+
+ for (i = 0; i < MAX_MODIFIERS; ++i)
+ if (cbres->pressed_modifiers[i])
+ return 1;
+
+ return 0;
+}
+
+void
+record_main_loop(Display * display, double idle_time)
+{
+
+ struct xrecord_callback_results cbres;
+ XRecordContext context;
+ XRecordClientSpec cspec = XRecordAllClients;
+ Display *dpy_data;
+ XRecordRange *range;
+ int i;
+
+ dpy_data = XOpenDisplay(NULL); /* we need an additional data connection. */
+ range = XRecordAllocRange();
+
+ range->device_events.first = KeyPress;
+ range->device_events.last = KeyRelease;
+
+ context = XRecordCreateContext(dpy_data, 0, &cspec, 1, &range, 1);
+
+ XRecordEnableContextAsync(dpy_data, context, xrecord_callback,
+ (XPointer) & cbres);
+
+ cbres.modifiers = XGetModifierMapping(display);
+ /* clear list of modifiers */
+ for (i = 0; i < MAX_MODIFIERS; ++i)
+ cbres.pressed_modifiers[i] = 0;
+
+ while (1) {
+
+ int fd = ConnectionNumber(dpy_data);
+ fd_set read_fds;
+ int ret;
+ int disable_event = 0;
+ struct timeval timeout;
+
+ FD_ZERO(&read_fds);
+ FD_SET(fd, &read_fds);
+
+ ret = select(fd + 1 /* =(max descriptor in read_fds) + 1 */ ,
+ &read_fds, NULL, NULL,
+ pad_disabled ? &timeout : NULL
+ /* timeout only required for enabling */ );
+
+ if (FD_ISSET(fd, &read_fds)) {
+
+ cbres.key_event = 0;
+ cbres.non_modifier_event = 0;
+
+ XRecordProcessReplies(dpy_data);
+
+ /* If there are any events left over, they are in error. Drain them
+ * from the connection queue so we don't get stuck. */
+ while (XEventsQueued(dpy_data, QueuedAlready) > 0) {
+ XEvent event;
+
+ XNextEvent(dpy_data, &event);
+ fprintf(stderr, "bad event received, major opcode %d\n",
+ event.type);
+ }
+
+ if (!ignore_modifier_keys && cbres.key_event) {
+ disable_event = 1;
+ }
+
+ if (cbres.non_modifier_event &&
+ !(ignore_modifier_combos && is_modifier_pressed(&cbres))) {
+ disable_event = 1;
+ }
+ }
+
+ if (disable_event) {
+ /* adjust the enable_time */
+ timeout.tv_sec = (int) idle_time;
+ timeout.tv_usec = (idle_time - (double) timeout.tv_sec) * 1.e6;
+
+ toggle_touchpad(False);
+ }
+
+ if (ret == 0 && pad_disabled) { /* timeout => enable event */
+ toggle_touchpad(True);
+ }
+
+ } /* end while(1) */
+
+ XFreeModifiermap(cbres.modifiers);
+}
+#endif /* HAVE_X11_EXTENSIONS_RECORD_H */
+
+static XDevice *
+dp_get_device(Display * dpy)
+{
+ XDevice *dev = NULL;
+ XDeviceInfo *info = NULL;
+ int ndevices = 0;
+ Atom touchpad_type = 0;
+ Atom *properties = NULL;
+ int nprops = 0;
+ int error = 0;
+
+ touchpad_type = XInternAtom(dpy, XI_TOUCHPAD, True);
+ touchpad_off_prop = XInternAtom(dpy, SYNAPTICS_PROP_OFF, True);
+ info = XListInputDevices(dpy, &ndevices);
+
+ while (ndevices--) {
+ if (info[ndevices].type == touchpad_type) {
+ dev = XOpenDevice(dpy, info[ndevices].id);
+ if (!dev) {
+ fprintf(stderr, "Failed to open device '%s'.\n",
+ info[ndevices].name);
+ error = 1;
+ goto unwind;
+ }
+
+ properties = XListDeviceProperties(dpy, dev, &nprops);
+ if (!properties || !nprops) {
+ fprintf(stderr, "No properties on device '%s'.\n",
+ info[ndevices].name);
+ error = 1;
+ goto unwind;
+ }
+
+ while (nprops--) {
+ if (properties[nprops] == touchpad_off_prop)
+ break;
+ }
+ if (nprops < 0) {
+ fprintf(stderr, "No synaptics properties on device '%s'.\n",
+ info[ndevices].name);
+ error = 1;
+ goto unwind;
+ }
+
+ break; /* Yay, device is suitable */
+ }
+ }
+
+ unwind:
+ XFree(properties);
+ XFreeDeviceList(info);
+ if (!dev)
+ fprintf(stderr, "Unable to find a synaptics device.\n");
+ else if (error && dev) {
+ XCloseDevice(dpy, dev);
+ dev = NULL;
+ }
+ return dev;
+}
+
+int
+main(int argc, char *argv[])
+{
+ double idle_time = 2.0;
+ int poll_delay = 200000; /* 200 ms */
+ int c;
+ int use_xrecord = 0;
+
+ /* Parse command line parameters */
+ while ((c = getopt(argc, argv, "i:m:dtp:kKR?v")) != EOF) {
+ switch (c) {
+ case 'i':
+ idle_time = atof(optarg);
+ break;
+ case 'm':
+ poll_delay = atoi(optarg) * 1000;
+ break;
+ case 'd':
+ background = 1;
+ break;
+ case 't':
+ disable_state = TappingOff;
+ break;
+ case 'p':
+ pid_file = optarg;
+ break;
+ case 'k':
+ ignore_modifier_keys = 1;
+ break;
+ case 'K':
+ ignore_modifier_combos = 1;
+ ignore_modifier_keys = 1;
+ break;
+ case 'R':
+ use_xrecord = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ if (idle_time <= 0.0)
+ usage();
+
+ /* Open a connection to the X server */
+ display = XOpenDisplay(NULL);
+ if (!display) {
+ fprintf(stderr, "Can't open display.\n");
+ exit(2);
+ }
+
+ if (!(dev = dp_get_device(display)))
+ exit(2);
+
+ /* Install a signal handler to restore synaptics parameters on exit */
+ install_signal_handler();
+
+ if (background) {
+ pid_t pid;
+
+ if ((pid = fork()) < 0) {
+ perror("fork");
+ exit(3);
+ }
+ else if (pid != 0)
+ exit(0);
+
+ /* Child (daemon) is running here */
+ setsid(); /* Become session leader */
+ chdir("/"); /* In case the file system gets unmounted */
+ umask(0); /* We don't want any surprises */
+ if (pid_file) {
+ FILE *fd = fopen(pid_file, "w");
+
+ if (!fd) {
+ perror("Can't create pid file");
+ exit(3);
+ }
+ fprintf(fd, "%d\n", getpid());
+ fclose(fd);
+ }
+ }
+
+ pad_disabled = False;
+ store_current_touchpad_state();
+
+#ifdef HAVE_X11_EXTENSIONS_RECORD_H
+ if (use_xrecord) {
+ if (check_xrecord(display))
+ record_main_loop(display, idle_time);
+ else {
+ fprintf(stderr, "Use of XRecord requested, but failed to "
+ " initialize.\n");
+ exit(4);
+ }
+ }
+ else
+#endif /* HAVE_X11_EXTENSIONS_RECORD_H */
+ {
+ setup_keyboard_mask(display, ignore_modifier_keys);
+
+ /* Run the main loop */
+ main_loop(display, idle_time, poll_delay);
+ }
+ return 0;
+}
+
+/* vim: set noexpandtab tabstop=8 shiftwidth=4: */
diff --git a/xorg-synaptics.pc.in b/xorg-synaptics.pc.in
new file mode 100644
index 0000000..159cfbf
--- /dev/null
+++ b/xorg-synaptics.pc.in
@@ -0,0 +1,6 @@
+sdkdir=@sdkdir@
+
+Name: synaptics
+Description: X.Org synaptics input driver.
+Version: @PACKAGE_VERSION@
+Cflags: -I${sdkdir}