diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-11-06 21:15:37 -0800 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-11-06 21:15:37 -0800 |
commit | f284c5b66d28e14e5934c2cac066afa1c9f49a88 (patch) | |
tree | 345168db809340cb1b7b87facacc54982d8020af | |
download | xf86-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
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' +# @@ -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 @@ -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 & !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 & 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, ¶->tap_time); + prop_tap_move = + InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_MOVE, 32, 1, ¶->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, ¶->clickpad); + prop_tap_fast = + InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_FAST, 8, 1, ¶->fast_taps); + prop_middle_timeout = + InitAtom(pInfo->dev, SYNAPTICS_PROP_MIDDLE_TIMEOUT, 32, 1, + ¶->emulate_mid_button_time); + prop_twofinger_pressure = + InitAtom(pInfo->dev, SYNAPTICS_PROP_TWOFINGER_PRESSURE, 32, 1, + ¶->emulate_twofinger_z); + prop_twofinger_width = + InitAtom(pInfo->dev, SYNAPTICS_PROP_TWOFINGER_WIDTH, 32, 1, + ¶->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, + ¶->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, + ¶->scroll_button_repeat); + } + + prop_off = + InitAtom(pInfo->dev, SYNAPTICS_PROP_OFF, 8, 1, ¶->touchpad_off); + prop_lockdrags = + InitAtom(pInfo->dev, SYNAPTICS_PROP_LOCKED_DRAGS, 8, 1, + ¶->locked_drags); + prop_lockdrags_time = + InitAtom(pInfo->dev, SYNAPTICS_PROP_LOCKED_DRAGS_TIMEOUT, 32, 1, + ¶->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, + ¶->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, + ¶->circular_trigger); + prop_circpad = + InitAtom(pInfo->dev, SYNAPTICS_PROP_CIRCULAR_PAD, 8, 1, + ¶->circular_pad); + prop_palm = + InitAtom(pInfo->dev, SYNAPTICS_PROP_PALM_DETECT, 8, 1, + ¶->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, + ¶->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 = ¶ms[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 = ¶ms[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} |