diff options
author | Alexandr Shadchin <shadchin@cvs.openbsd.org> | 2011-04-09 08:38:43 +0000 |
---|---|---|
committer | Alexandr Shadchin <shadchin@cvs.openbsd.org> | 2011-04-09 08:38:43 +0000 |
commit | d7e280ac3a05f25fa5b098f203aa40e92d040a5d (patch) | |
tree | e716a94142e8509677a0147ad6242d5072351dc8 /driver/xf86-input-synaptics/src | |
parent | b9f5f7ebfd9cf7882a73f6072406855aae88cc9e (diff) |
Import xf86-input-synaptics 1.4.0
ok matthieu@
Diffstat (limited to 'driver/xf86-input-synaptics/src')
-rw-r--r-- | driver/xf86-input-synaptics/src/Makefile.am | 49 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/Makefile.in | 547 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/alpscomm.c | 237 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/alpscomm.h | 33 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/eventcomm.c | 499 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/eventcomm.h | 37 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/properties.c | 673 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/ps2comm.c | 676 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/ps2comm.h | 103 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/psmcomm.c | 185 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/synaptics.c | 2661 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/synapticsstr.h | 249 | ||||
-rw-r--r-- | driver/xf86-input-synaptics/src/synproto.h | 106 |
13 files changed, 6055 insertions, 0 deletions
diff --git a/driver/xf86-input-synaptics/src/Makefile.am b/driver/xf86-input-synaptics/src/Makefile.am new file mode 100644 index 000000000..980ab5ef7 --- /dev/null +++ b/driver/xf86-input-synaptics/src/Makefile.am @@ -0,0 +1,49 @@ +# 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 \ + alpscomm.c alpscomm.h \ + ps2comm.c ps2comm.h \ + synproto.h \ + properties.c + +if BUILD_EVENTCOMM +@DRIVER_NAME@_drv_la_SOURCES += \ + eventcomm.c eventcomm.h +endif + +if BUILD_PSMCOMM +@DRIVER_NAME@_drv_la_SOURCES += \ + psmcomm.c +endif + diff --git a/driver/xf86-input-synaptics/src/Makefile.in b/driver/xf86-input-synaptics/src/Makefile.in new file mode 100644 index 000000000..0e170333f --- /dev/null +++ b/driver/xf86-input-synaptics/src/Makefile.in @@ -0,0 +1,547 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# 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. + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@BUILD_EVENTCOMM_TRUE@am__append_1 = \ +@BUILD_EVENTCOMM_TRUE@ eventcomm.c eventcomm.h + +@BUILD_PSMCOMM_TRUE@am__append_2 = \ +@BUILD_PSMCOMM_TRUE@ psmcomm.c + +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)" +@DRIVER_NAME@_drv_laLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(@DRIVER_NAME@_drv_la_LTLIBRARIES) +@DRIVER_NAME@_drv_la_LIBADD = +am__@DRIVER_NAME@_drv_la_SOURCES_DIST = @DRIVER_NAME@.c synapticsstr.h \ + alpscomm.c alpscomm.h ps2comm.c ps2comm.h synproto.h \ + properties.c eventcomm.c eventcomm.h psmcomm.c +@BUILD_EVENTCOMM_TRUE@am__objects_1 = eventcomm.lo +@BUILD_PSMCOMM_TRUE@am__objects_2 = psmcomm.lo +am_@DRIVER_NAME@_drv_la_OBJECTS = @DRIVER_NAME@.lo alpscomm.lo \ + ps2comm.lo properties.lo $(am__objects_1) $(am__objects_2) +@DRIVER_NAME@_drv_la_OBJECTS = $(am_@DRIVER_NAME@_drv_la_OBJECTS) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(@DRIVER_NAME@_drv_la_SOURCES) +DIST_SOURCES = $(am__@DRIVER_NAME@_drv_la_SOURCES_DIST) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ADMIN_MAN_DIR = @ADMIN_MAN_DIR@ +ADMIN_MAN_SUFFIX = @ADMIN_MAN_SUFFIX@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APP_MAN_DIR = @APP_MAN_DIR@ +APP_MAN_SUFFIX = @APP_MAN_SUFFIX@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EVENTCOMM_FALSE = @BUILD_EVENTCOMM_FALSE@ +BUILD_EVENTCOMM_TRUE = @BUILD_EVENTCOMM_TRUE@ +BUILD_PSMCOMM_FALSE = @BUILD_PSMCOMM_FALSE@ +BUILD_PSMCOMM_TRUE = @BUILD_PSMCOMM_TRUE@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CHANGELOG_CMD = @CHANGELOG_CMD@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CWARNFLAGS = @CWARNFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEBUG_FALSE = @DEBUG_FALSE@ +DEBUG_TRUE = @DEBUG_TRUE@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DRIVER_MAN_DIR = @DRIVER_MAN_DIR@ +DRIVER_MAN_SUFFIX = @DRIVER_MAN_SUFFIX@ +DRIVER_NAME = @DRIVER_NAME@ +DSYMUTIL = @DSYMUTIL@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +FILE_MAN_DIR = @FILE_MAN_DIR@ +FILE_MAN_SUFFIX = @FILE_MAN_SUFFIX@ +GREP = @GREP@ +HAS_XORG_CONF_DIR_FALSE = @HAS_XORG_CONF_DIR_FALSE@ +HAS_XORG_CONF_DIR_TRUE = @HAS_XORG_CONF_DIR_TRUE@ +INSTALL_CMD = @INSTALL_CMD@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIB_MAN_DIR = @LIB_MAN_DIR@ +LIB_MAN_SUFFIX = @LIB_MAN_SUFFIX@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +MAN_SUBSTS = @MAN_SUBSTS@ +MISC_MAN_DIR = @MISC_MAN_DIR@ +MISC_MAN_SUFFIX = @MISC_MAN_SUFFIX@ +NMEDIT = @NMEDIT@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +XI_CFLAGS = @XI_CFLAGS@ +XI_LIBS = @XI_LIBS@ +XORG_CFLAGS = @XORG_CFLAGS@ +XORG_LIBS = @XORG_LIBS@ +XORG_MAN_PAGE = @XORG_MAN_PAGE@ +XTST_CFLAGS = @XTST_CFLAGS@ +XTST_LIBS = @XTST_LIBS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +configdir = @configdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +inputdir = @inputdir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sdkdir = @sdkdir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ + +# 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 \ + alpscomm.c alpscomm.h ps2comm.c ps2comm.h synproto.h \ + properties.c $(am__append_1) $(am__append_2) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-@DRIVER_NAME@_drv_laLTLIBRARIES: $(@DRIVER_NAME@_drv_la_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(@DRIVER_NAME@_drv_ladir)" || $(mkdir_p) "$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)" + @list='$(@DRIVER_NAME@_drv_la_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=install $(@DRIVER_NAME@_drv_laLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)/$$f'"; \ + $(LIBTOOL) --mode=install $(@DRIVER_NAME@_drv_laLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)/$$f"; \ + else :; fi; \ + done + +uninstall-@DRIVER_NAME@_drv_laLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @set -x; list='$(@DRIVER_NAME@_drv_la_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)/$$p'"; \ + $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)/$$p"; \ + done + +clean-@DRIVER_NAME@_drv_laLTLIBRARIES: + -test -z "$(@DRIVER_NAME@_drv_la_LTLIBRARIES)" || rm -f $(@DRIVER_NAME@_drv_la_LTLIBRARIES) + @list='$(@DRIVER_NAME@_drv_la_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +@DRIVER_NAME@_drv.la: $(@DRIVER_NAME@_drv_la_OBJECTS) $(@DRIVER_NAME@_drv_la_DEPENDENCIES) + $(LINK) -rpath $(@DRIVER_NAME@_drv_ladir) $(@DRIVER_NAME@_drv_la_LDFLAGS) $(@DRIVER_NAME@_drv_la_OBJECTS) $(@DRIVER_NAME@_drv_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/@DRIVER_NAME@.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alpscomm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eventcomm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/properties.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ps2comm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psmcomm.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(@DRIVER_NAME@_drv_ladir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-@DRIVER_NAME@_drv_laLTLIBRARIES clean-generic \ + clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-@DRIVER_NAME@_drv_laLTLIBRARIES + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-@DRIVER_NAME@_drv_laLTLIBRARIES \ + uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean \ + clean-@DRIVER_NAME@_drv_laLTLIBRARIES clean-generic \ + clean-libtool ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install \ + install-@DRIVER_NAME@_drv_laLTLIBRARIES install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-@DRIVER_NAME@_drv_laLTLIBRARIES \ + uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/driver/xf86-input-synaptics/src/alpscomm.c b/driver/xf86-input-synaptics/src/alpscomm.c new file mode 100644 index 000000000..84d213656 --- /dev/null +++ b/driver/xf86-input-synaptics/src/alpscomm.c @@ -0,0 +1,237 @@ +/* + * 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 "alpscomm.h" +#include "synproto.h" +#include "synaptics.h" +#include "synapticsstr.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; + + 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 SynapticsProtocolOperations *proto_ops, + 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); + + *hwRet = *hw; + return TRUE; +} + +static Bool +ALPSAutoDevProbe(InputInfoPtr pInfo) +{ + return FALSE; +} + +struct SynapticsProtocolOperations alps_proto_operations = { + NULL, + NULL, + ALPSQueryHardware, + ALPSReadHwState, + ALPSAutoDevProbe, + SynapticsDefaultDimensions +}; diff --git a/driver/xf86-input-synaptics/src/alpscomm.h b/driver/xf86-input-synaptics/src/alpscomm.h new file mode 100644 index 000000000..547cf72ba --- /dev/null +++ b/driver/xf86-input-synaptics/src/alpscomm.h @@ -0,0 +1,33 @@ +/* + * 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 _ALPSCOMM_H_ +#define _ALPSCOMM_H_ + +#include "ps2comm.h" + + +#endif /* _ALPSCOMM_H_ */ diff --git a/driver/xf86-input-synaptics/src/eventcomm.c b/driver/xf86-input-synaptics/src/eventcomm.c new file mode 100644 index 000000000..1a31c544f --- /dev/null +++ b/driver/xf86-input-synaptics/src/eventcomm.c @@ -0,0 +1,499 @@ +/* + * 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> + + +#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) + +/***************************************************************************** + * Function Definitions + ****************************************************************************/ + +static void +EventDeviceOnHook(InputInfoPtr pInfo, SynapticsParameters *para) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + BOOL *need_grab; + + if (!priv->proto_data) + priv->proto_data = calloc(1, sizeof(BOOL)); + + need_grab = (BOOL*)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) { + xf86Msg(X_WARNING, "%s can't grab event device, errno=%d\n", + pInfo->name, errno); + } + } + + *need_grab = FALSE; +} + +static Bool +event_query_is_touchpad(int fd, BOOL 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 (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 (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}, + {0x0, 0x0, 0x0} +}; + +static void +event_query_info(InputInfoPtr pInfo) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + short id[4]; + int rc; + model_lookup_t *model_lookup; + + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGID, id)); + if (rc < 0) + return; + + for(model_lookup = model_lookup_table; model_lookup->vendor; model_lookup++) { + if(model_lookup->vendor == id[ID_VENDOR] && + (model_lookup->product == id[ID_PRODUCT] || model_lookup->product == PRODUCT_ANY)) + priv->model = model_lookup->model; + } +} + +/* Query device for axis ranges */ +static void +event_query_axis_ranges(InputInfoPtr pInfo) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + struct input_absinfo abs = {0}; + unsigned long absbits[NBITS(ABS_MAX)] = {0}; + unsigned long keybits[NBITS(KEY_MAX)] = {0}; + char buf[256]; + int rc; + + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGABS(ABS_X), &abs)); + if (rc >= 0) + { + xf86Msg(X_PROBED, "%s: x-axis range %d - %d\n", pInfo->name, + abs.minimum, abs.maximum); + priv->minx = abs.minimum; + priv->maxx = abs.maximum; + /* 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. Though, + * we don't trust a zero fuzz as it probably is just a lazy value. */ + if (abs.fuzz > 0) + priv->synpara.hyst_x = abs.fuzz; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30) + priv->resx = abs.resolution; +#endif + } else + xf86Msg(X_ERROR, "%s: failed to query axis range (%s)\n", pInfo->name, + strerror(errno)); + + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGABS(ABS_Y), &abs)); + if (rc >= 0) + { + xf86Msg(X_PROBED, "%s: y-axis range %d - %d\n", pInfo->name, + abs.minimum, abs.maximum); + priv->miny = abs.minimum; + priv->maxy = abs.maximum; + /* don't trust a zero fuzz */ + if (abs.fuzz > 0) + priv->synpara.hyst_y = abs.fuzz; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30) + priv->resy = abs.resolution; +#endif + } else + xf86Msg(X_ERROR, "%s: failed to query axis range (%s)\n", pInfo->name, + strerror(errno)); + + 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 + xf86Msg(X_ERROR, "%s: failed to query ABS bits (%s)\n", pInfo->name, + strerror(errno)); + + if (priv->has_pressure) + { + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGABS(ABS_PRESSURE), &abs)); + if (rc >= 0) + { + xf86Msg(X_PROBED, "%s: pressure range %d - %d\n", pInfo->name, + abs.minimum, abs.maximum); + priv->minp = abs.minimum; + priv->maxp = abs.maximum; + } + } else + xf86Msg(X_INFO, + "%s: device does not report pressure, will use touch data.\n", + pInfo->name); + + if (priv->has_width) + { + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGABS(ABS_TOOL_WIDTH), &abs)); + if (rc >= 0) + { + xf86Msg(X_PROBED, "%s: finger width range %d - %d\n", pInfo->name, + abs.minimum, abs.maximum); + priv->minw = abs.minimum; + priv->maxw = abs.maximum; + } + } + + SYSCALL(rc = ioctl(pInfo->fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits)); + if (rc >= 0) + { + buf[0] = 0; + if ((priv->has_left = (TEST_BIT(BTN_LEFT, keybits) != 0))) + strcat(buf, " left"); + if ((priv->has_right = (TEST_BIT(BTN_RIGHT, keybits) != 0))) + strcat(buf, " right"); + if ((priv->has_middle = (TEST_BIT(BTN_MIDDLE, keybits) != 0))) + strcat(buf, " middle"); + if ((priv->has_double = (TEST_BIT(BTN_TOOL_DOUBLETAP, keybits) != 0))) + strcat(buf, " double"); + if ((priv->has_triple = (TEST_BIT(BTN_TOOL_TRIPLETAP, keybits) != 0))) + strcat(buf, " triple"); + + 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; + strcat(buf, " scroll-buttons"); + } + + xf86Msg(X_PROBED, "%s: buttons:%s\n", pInfo->name, buf); + } +} + +static Bool +EventQueryHardware(InputInfoPtr pInfo) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + BOOL *need_grab = (BOOL*)priv->proto_data; + + if (!event_query_is_touchpad(pInfo->fd, (need_grab) ? *need_grab : TRUE)) + return FALSE; + + xf86Msg(X_PROBED, "%s: touchpad found\n", pInfo->name); + + return TRUE; +} + +static Bool +SynapticsReadEvent(InputInfoPtr pInfo, struct input_event *ev) +{ + int rc = TRUE; + ssize_t len; + + 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; +} + +static Bool +EventReadHwState(InputInfoPtr pInfo, + struct SynapticsProtocolOperations *proto_ops, + 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; + + while (SynapticsReadEvent(pInfo, &ev)) { + switch (ev.type) { + case EV_SYN: + switch (ev.code) { + case SYN_REPORT: + if (comm->oneFinger) + hw->numFingers = 1; + else if (comm->twoFingers) + hw->numFingers = 2; + else if (comm->threeFingers) + hw->numFingers = 3; + else + hw->numFingers = 0; + *hwRet = *hw; + return TRUE; + } + 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: + switch (ev.code) { + case ABS_X: + hw->x = ev.value; + break; + case ABS_Y: + hw->y = ev.value; + break; + case ABS_PRESSURE: + hw->z = ev.value; + break; + case ABS_TOOL_WIDTH: + hw->fingerWidth = ev.value; + break; + } + 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; +} + +/** + * Probe the open device for dimensions. + */ +static void +EventReadDevDimensions(InputInfoPtr pInfo) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + BOOL *need_grab = (BOOL*)priv->proto_data; + + if (event_query_is_touchpad(pInfo->fd, (need_grab) ? *need_grab : TRUE)) + event_query_axis_ranges(pInfo); + event_query_info(pInfo); +} + +static Bool +EventAutoDevProbe(InputInfoPtr pInfo) +{ + /* 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; + + i = scandir(DEV_INPUT_EVENT, &namelist, EventDevOnly, alphasort); + if (i < 0) { + xf86Msg(X_ERROR, "Couldn't open %s\n", DEV_INPUT_EVENT); + return FALSE; + } + else if (i == 0) { + xf86Msg(X_ERROR, "%s The /dev/input/event* device nodes seem to be missing\n", + pInfo->name); + 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; + xf86Msg(X_PROBED, "%s auto-dev sets device to %s\n", + pInfo->name, fname); + pInfo->options = + xf86ReplaceStrOption(pInfo->options, "Device", fname); + } + SYSCALL(close(fd)); + } + free(namelist[i]); + } + free(namelist); + + if (!touchpad_found) { + xf86Msg(X_ERROR, "%s no synaptics event device found\n", pInfo->name); + return FALSE; + } + return TRUE; +} + +struct SynapticsProtocolOperations event_proto_operations = { + EventDeviceOnHook, + NULL, + EventQueryHardware, + EventReadHwState, + EventAutoDevProbe, + EventReadDevDimensions +}; diff --git a/driver/xf86-input-synaptics/src/eventcomm.h b/driver/xf86-input-synaptics/src/eventcomm.h new file mode 100644 index 000000000..8fd7bcbb0 --- /dev/null +++ b/driver/xf86-input-synaptics/src/eventcomm.h @@ -0,0 +1,37 @@ +/* + * 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> + +/* for auto-dev: */ +#define DEV_INPUT_EVENT "/dev/input" +#define EVENT_DEV_NAME "event" + +#endif /* _EVENTCOMM_H_ */ diff --git a/driver/xf86-input-synaptics/src/properties.c b/driver/xf86-input-synaptics/src/properties.c new file mode 100644 index 000000000..23b5a6a4c --- /dev/null +++ b/driver/xf86-input-synaptics/src/properties.c @@ -0,0 +1,673 @@ +/* + * 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 +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_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_noise_cancellation = 0; + +static Atom +InitAtom(DeviceIntPtr dev, char *name, 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, XA_INTEGER, format, + PropModeReplace, nvalues, + converted, FALSE); + XISetDevicePropertyDeletable(dev, atom, FALSE); + return atom; +} + +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; +} + +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) + { + xf86Msg(X_ERROR, "%s: Failed to init float atom. " + "Disabling property support.\n", pInfo->name); + 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_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 = InitAtom(pInfo->dev, SYNAPTICS_PROP_PRESSURE_MOTION, 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); + + values[0] = para->hyst_x; + values[1] = para->hyst_y; + prop_noise_cancellation = InitAtom(pInfo->dev, + SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 2, values); + +} + +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_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; + para->scroll_dist_vert = dist[0]; + para->scroll_dist_horiz = dist[1]; + } 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; + 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) + { + float *press; + if (prop->size != 2 || prop->format != 32 || prop->type != float_type) + return BadMatch; + + press = (float*)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_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]; + } + + return Success; +} + diff --git a/driver/xf86-input-synaptics/src/ps2comm.c b/driver/xf86-input-synaptics/src/ps2comm.c new file mode 100644 index 000000000..4e372b371 --- /dev/null +++ b/driver/xf86-input-synaptics/src/ps2comm.c @@ -0,0 +1,676 @@ +/* + * 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 "ps2comm.h" +#include "synproto.h" +#include "synaptics.h" +#include "synapticsstr.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 + +struct SynapticsHwInfo { + unsigned int model_id; /* Model-ID */ + unsigned int capabilities; /* Capabilities */ + unsigned int ext_cap; /* Extended Capabilities */ + unsigned int identity; /* Identification */ +}; + +/***************************************************************************** + * 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 SynapticsHwInfo *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 SynapticsHwInfo *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 SynapticsHwInfo *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(int fd, struct SynapticsHwInfo* 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 { + xf86Msg(X_ERROR, "Query no Synaptics: %06X\n", synhw->identity); + return FALSE; + } +} + +void +ps2_print_ident(const struct SynapticsHwInfo *synhw) +{ + xf86Msg(X_PROBED, " Synaptics Touchpad, model: %d\n", SYN_ID_MODEL(synhw)); + xf86Msg(X_PROBED, " Firmware: %d.%d\n", SYN_ID_MAJOR(synhw), + SYN_ID_MINOR(synhw)); + + if (SYN_MODEL_ROT180(synhw)) + xf86Msg(X_PROBED, " 180 degree mounted touchpad\n"); + if (SYN_MODEL_PORTRAIT(synhw)) + xf86Msg(X_PROBED, " portrait touchpad\n"); + xf86Msg(X_PROBED, " Sensor: %d\n", SYN_MODEL_SENSOR(synhw)); + if (SYN_MODEL_NEWABS(synhw)) + xf86Msg(X_PROBED, " new absolute packet format\n"); + if (SYN_MODEL_PEN(synhw)) + xf86Msg(X_PROBED, " pen detection\n"); + + if (SYN_CAP_EXTENDED(synhw)) { + xf86Msg(X_PROBED, " Touchpad has extended capability bits\n"); + if (SYN_CAP_MULTI_BUTTON_NO(synhw)) + xf86Msg(X_PROBED, " -> %d multi buttons, i.e. besides standard buttons\n", + (int)(SYN_CAP_MULTI_BUTTON_NO(synhw))); + if (SYN_CAP_MIDDLE_BUTTON(synhw)) + xf86Msg(X_PROBED, " -> middle button\n"); + if (SYN_CAP_FOUR_BUTTON(synhw)) + xf86Msg(X_PROBED, " -> four buttons\n"); + if (SYN_CAP_MULTIFINGER(synhw)) + xf86Msg(X_PROBED, " -> multifinger detection\n"); + if (SYN_CAP_PALMDETECT(synhw)) + xf86Msg(X_PROBED, " -> palm detection\n"); + if (SYN_CAP_PASSTHROUGH(synhw)) + xf86Msg(X_PROBED, " -> pass-through port\n"); + } +} + +static void +PS2DeviceOffHook(InputInfoPtr pInfo) +{ + ps2_synaptics_reset(pInfo->fd); + ps2_synaptics_enable_device(pInfo->fd); +} + +static Bool +PS2QueryHardware(InputInfoPtr pInfo) +{ + int mode; + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + struct SynapticsHwInfo *synhw; + + if (!priv->proto_data) + priv->proto_data = calloc(1, sizeof(struct SynapticsHwInfo)); + synhw = (struct SynapticsHwInfo*)priv->proto_data; + + /* is the synaptics touchpad active? */ + if (!ps2_query_is_synaptics(pInfo->fd, synhw)) + return FALSE; + + xf86Msg(X_PROBED, "%s synaptics touchpad found\n", pInfo->name); + + if (!ps2_synaptics_reset(pInfo->fd)) + xf86Msg(X_ERROR, "%s reset failed\n", pInfo->name); + + 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(synhw); + + return TRUE; +} + +/* + * Decide if the current packet stored in priv->protoBuf is valid. + */ +static Bool +ps2_packet_ok(struct SynapticsHwInfo *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 SynapticsHwInfo *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) { + xf86Msg(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; +} + +static Bool +PS2ReadHwState(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 SynapticsHwInfo *synhw; + int newabs; + int w, i; + + synhw = (struct SynapticsHwInfo*)priv->proto_data; + if (!synhw) + { + xf86Msg(X_ERROR, + "%s: PS2ReadHwState, synhw is NULL. This is a bug.\n", + pInfo->name); + 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; + } + } + + *hwRet = *hw; + return TRUE; +} + +static Bool +PS2AutoDevProbe(InputInfoPtr pInfo) +{ + return FALSE; +} + +struct SynapticsProtocolOperations psaux_proto_operations = { + NULL, + PS2DeviceOffHook, + PS2QueryHardware, + PS2ReadHwState, + PS2AutoDevProbe, + SynapticsDefaultDimensions +}; diff --git a/driver/xf86-input-synaptics/src/ps2comm.h b/driver/xf86-input-synaptics/src/ps2comm.h new file mode 100644 index 000000000..fec56346d --- /dev/null +++ b/driver/xf86-input-synaptics/src/ps2comm.h @@ -0,0 +1,103 @@ +/* + * 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) == 0x100000) +#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; + + +Bool ps2_putbyte(int fd, byte b); + +struct SynapticsHwInfo; +void ps2_print_ident(const struct SynapticsHwInfo *synhw); + +#endif /* _PS2COMM_H_ */ diff --git a/driver/xf86-input-synaptics/src/psmcomm.c b/driver/xf86-input-synaptics/src/psmcomm.c new file mode 100644 index 000000000..741cd1d20 --- /dev/null +++ b/driver/xf86-input-synaptics/src/psmcomm.c @@ -0,0 +1,185 @@ +/* + * 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)) + +struct SynapticsHwInfo { + unsigned int model_id; /* Model-ID */ + unsigned int capabilities; /* Capabilities */ + unsigned int ext_cap; /* Extended Capabilities */ + unsigned int identity; /* Identification */ +}; + +/* + * 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) { + xf86Msg(X_ERROR, "%s Can't set native mode\n", pInfo->name); + return FALSE; + } + SYSCALL(ret = ioctl(pInfo->fd, MOUSE_GETHWINFO, &mhw)); + if (ret != 0) { + xf86Msg(X_ERROR, "%s Can't get hardware info\n", pInfo->name); + return FALSE; + } + + if (mhw.model == MOUSE_MODEL_SYNAPTICS) { + return TRUE; + } else { + xf86Msg(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 SynapticsHwInfo *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 SynapticsHwInfo *synhw; + SynapticsPrivate *priv; + + priv = (SynapticsPrivate *)pInfo->private; + + if(!priv->proto_data) + priv->proto_data = calloc(1, sizeof(struct SynapticsHwInfo)); + synhw = (struct SynapticsHwInfo*)priv->proto_data; + + /* is the synaptics touchpad active? */ + if (!PSMQueryIsSynaptics(pInfo)) + return FALSE; + + xf86Msg(X_PROBED, "%s synaptics touchpad found\n", pInfo->name); + + if (!psm_synaptics_identify(pInfo->fd, &psm_ident)) + return FALSE; + + convert_hw_info(&psm_ident, synhw); + + ps2_print_ident(synhw); + + return TRUE; +} + +static Bool +PSMReadHwState(InputInfoPtr pInfo, + struct SynapticsProtocolOperations *proto_ops, + struct CommData *comm, struct SynapticsHwState *hwRet) +{ + return psaux_proto_operations.ReadHwState(pInfo, proto_ops, comm, hwRet); +} + +static Bool PSMAutoDevProbe(InputInfoPtr pInfo) +{ + return FALSE; +} + +struct SynapticsProtocolOperations psm_proto_operations = { + NULL, + NULL, + PSMQueryHardware, + PSMReadHwState, + PSMAutoDevProbe, + SynapticsDefaultDimensions +}; diff --git a/driver/xf86-input-synaptics/src/synaptics.c b/driver/xf86-input-synaptics/src/synaptics.c new file mode 100644 index 000000000..56ce725e6 --- /dev/null +++ b/driver/xf86-input-synaptics/src/synaptics.c @@ -0,0 +1,2661 @@ +/* + * 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. + * + * 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 "synaptics.h" +#include "synapticsstr.h" +#include "synaptics-properties.h" + +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 +#include <X11/Xatom.h> +#include <xserver-properties.h> +#include <ptrveloc.h> +#endif + +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; + +#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*); +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); + +void InitDeviceProperties(InputInfoPtr pInfo); +int SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, + BOOL checkonly); + +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. + */ +void +SynapticsDefaultDimensions(InputInfoPtr pInfo) +{ + SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private; + + if (priv->minx >= priv->maxx) + { + priv->minx = 1615; + priv->maxx = 5685; + priv->resx = 0; + + xf86Msg(X_PROBED, + "%s: invalid x-axis range. defaulting to %d - %d\n", + pInfo->name, priv->minx, priv->maxx); + } + + if (priv->miny >= priv->maxy) + { + priv->miny = 1729; + priv->maxy = 4171; + priv->resx = 0; + + xf86Msg(X_PROBED, + "%s: invalid y-axis range. defaulting to %d - %d\n", + pInfo->name, priv->miny, priv->maxy); + } + + if (priv->minp >= priv->maxp) + { + priv->minp = 0; + priv->maxp = 256; + + xf86Msg(X_PROBED, + "%s: invalid pressure range. defaulting to %d - %d\n", + pInfo->name, priv->minp, priv->maxp); + } + + if (priv->minw >= priv->maxw) + { + priv->minw = 0; + priv->maxw = 16; + + xf86Msg(X_PROBED, + "%s: invalid finger width range. defaulting to %d - %d\n", + pInfo->name, priv->minw, priv->maxw); + } +} + +static void +SetDeviceAndProtocol(InputInfoPtr pInfo) +{ + char *str_par, *device; + SynapticsPrivate *priv = pInfo->private; + enum SynapticsProtocol proto = SYN_PROTO_PSAUX; + + device = xf86SetStrOption(pInfo->options, "Device", NULL); + if (!device) { + device = xf86SetStrOption(pInfo->options, "Path", NULL); + if (device) { + pInfo->options = + xf86ReplaceStrOption(pInfo->options, "Device", device); + } + } + if (device && strstr(device, "/dev/input/event")) { +#ifdef BUILD_EVENTCOMM + proto = SYN_PROTO_EVENT; +#endif + } else { + str_par = xf86FindOptionValue(pInfo->options, "Protocol"); + if (str_par && !strcmp(str_par, "psaux")) { + /* Already set up */ +#ifdef BUILD_EVENTCOMM + } else if (str_par && !strcmp(str_par, "event")) { + proto = SYN_PROTO_EVENT; +#endif /* BUILD_EVENTCOMM */ +#ifdef BUILD_PSMCOMM + } else if (str_par && !strcmp(str_par, "psm")) { + proto = SYN_PROTO_PSM; +#endif /* BUILD_PSMCOMM */ + } else if (str_par && !strcmp(str_par, "alps")) { + proto = SYN_PROTO_ALPS; + } else { /* default to auto-dev */ +#ifdef BUILD_EVENTCOMM + if (!device && event_proto_operations.AutoDevProbe(pInfo)) + proto = SYN_PROTO_EVENT; +#endif + } + } + switch (proto) { + case SYN_PROTO_PSAUX: + priv->proto_ops = &psaux_proto_operations; + break; +#ifdef BUILD_EVENTCOMM + case SYN_PROTO_EVENT: + priv->proto_ops = &event_proto_operations; + break; +#endif /* BUILD_EVENTCOMM */ +#ifdef BUILD_PSMCOMM + case SYN_PROTO_PSM: + priv->proto_ops = &psm_proto_operations; + break; +#endif /* BUILD_PSMCOMM */ + case SYN_PROTO_ALPS: + priv->proto_ops = &alps_proto_operations; + break; + } +} + +/* + * 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) { + xf86Msg(X_ERROR, "%s error shmget\n", pInfo->name); + return FALSE; + } + if ((priv->synshm = (SynapticsSHM*)shmat(shmid, NULL, 0)) == NULL) { + xf86Msg(X_ERROR, "%s error shmat\n", pInfo->name); + 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; +} + +/* 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; +} + +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; + + /* 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. + */ + SynapticsDefaultDimensions(pInfo); + + 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; + + /* scaling based on defaults and a pressure of 256 */ + fingerLow = priv->minp + range * (25.0/256); + fingerHigh = priv->minp + range * (30.0/256); + fingerPress = priv->minp + range * 1.000; + 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); + + /* Enable emulation when hw supports both pressure and width. */ + if (!priv->has_double && priv->has_width) + emulateTwoFingerMinZ = fingerHigh; + else + emulateTwoFingerMinZ = priv->minp + range * (282.0/256); + + range = priv->maxw - priv->minw; + + /* 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->fast_taps = xf86SetBoolOption(opts, "FastTaps", FALSE); + pars->emulate_mid_button_time = xf86SetIntOption(opts, "EmulateMidButtonTime", 75); + pars->emulate_twofinger_z = xf86SetIntOption(opts, "EmulateTwoFingerMinZ", emulateTwoFingerMinZ); + pars->emulate_twofinger_w = xf86SetIntOption(opts, "EmulateTwoFingerMinW", emulateTwoFingerMinW); + pars->scroll_dist_vert = xf86SetIntOption(opts, "VertScrollDelta", horizScrollDelta); + pars->scroll_dist_horiz = xf86SetIntOption(opts, "HorizScrollDelta", vertScrollDelta); + 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; + xf86Msg(X_WARNING, "%s: TopEdge is bigger than BottomEdge. Fixing.\n", + pInfo->name); + } +} + +static float SynapticsAccelerationProfile(DeviceIntPtr dev, + DeviceVelocityPtr vel, + float velocity, + float thr, + float acc) { + 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 */ + SetDeviceAndProtocol(pInfo); + + /* open the touchpad device */ + pInfo->fd = xf86OpenSerial(pInfo->options); + if (pInfo->fd == -1) { + xf86Msg(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)) { + xf86Msg(X_ERROR, "%s Unable to query/initialize Synaptics hardware.\n", pInfo->name); + 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); + 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"); + + SetDeviceAndProtocol(pInfo); + pInfo->fd = xf86OpenSerial(pInfo->options); + if (pInfo->fd == -1) { + xf86Msg(X_WARNING, "%s: cannot open input device\n", pInfo->name); + return !Success; + } + + if (priv->proto_ops->DeviceOnHook) + priv->proto_ops->DeviceOnHook(pInfo, &priv->synpara); + + 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 Bool +DeviceOff(DeviceIntPtr dev) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private); + + DBG(3, "Synaptics DeviceOff called\n"); + + if (pInfo->fd != -1) { + TimerCancel(priv->timer); + xf86RemoveEnabledDevice(pInfo); + if (priv->proto_ops->DeviceOffHook) + priv->proto_ops->DeviceOffHook(pInfo); + if (priv->comm.buffer) { + XisbFree(priv->comm.buffer); + priv->comm.buffer = NULL; + } + xf86CloseSerial(pInfo->fd); + pInfo->fd = -1; + } + dev->public.on = FALSE; + return Success; +} + +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); + return RetValue; +} + +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 +static void InitAxesLabels(Atom *labels, int nlabels) +{ + memset(labels, 0, nlabels * sizeof(Atom)); + switch(nlabels) + { + default: + case 2: + labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y); + case 1: + labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X); + break; + } +} + +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; + } +} +#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; +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + Atom btn_labels[SYN_MAX_BUTTONS] = { 0 }; + Atom axes_labels[2] = { 0 }; + DeviceVelocityPtr pVel; + + InitAxesLabels(axes_labels, 2); + InitButtonLabels(btn_labels, SYN_MAX_BUTTONS); +#endif + + 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, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + btn_labels, +#endif + SynapticsCtrl, + GetMotionHistorySize(), 2 +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + , axes_labels +#endif + ); + + /* + * setup dix acceleration to match legacy synaptics settings, and + * etablish a device-specific profile to do stuff like pressure-related + * acceleration. + */ +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + 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; + + xf86Msg(X_CONFIG, "%s: (accel) MinSpeed is now constant deceleration " + "%.1f\n", dev->name, 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 */ + + xf86Msg(X_CONFIG, "%s: MaxSpeed is now %.2f\n", + dev->name, priv->synpara.max_speed); + xf86Msg(X_CONFIG, "%s: AccelFactor is now %.3f\n", + dev->name, priv->synpara.accl); + + prop = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER); + i = AccelProfileDeviceSpecific; + XIChangeDeviceProperty(dev, prop, XA_INTEGER, 32, + PropModeReplace, 1, &i, FALSE); + } +#endif + + /* X valuator */ + if (priv->minx < priv->maxx) + { + min = priv->minx; + max = priv->maxx; + } else + { + min = 0; + max = -1; + } + + xf86InitValuatorAxisStruct(dev, 0, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + axes_labels[0], +#endif + 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, +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 + axes_labels[1], +#endif + min, max, priv->resy * 1000, 0, priv->resy * 1000 +#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12 + , Relative +#endif + ); + xf86InitValuatorDefaults(dev, 1); + + if (!alloc_shm_data(pInfo)) + return !Success; + + InitDeviceProperties(pInfo); + XIRegisterPropertyHandler(pInfo->dev, SetProperty, NULL, NULL); + + 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 CARD32 +timerFunc(OsTimerPtr timer, CARD32 now, pointer arg) +{ + InputInfoPtr pInfo = arg; + SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private); + struct SynapticsHwState hw; + int delay; + int sigstate; + CARD32 wakeUpTime; + + sigstate = xf86BlockSIGIO(); + + hw = priv->hwState; + hw.millis = now; + delay = HandleState(pInfo, &hw); + + /* + * Workaround for wraparound bug in the TimerSet function. This bug is already + * fixed in CVS, but this driver needs to work with XFree86 versions 4.2.x and + * 4.3.x too. + */ + wakeUpTime = now + delay; + if (wakeUpTime <= now) + wakeUpTime = 0xffffffffL; + + priv->timer = TimerSet(priv->timer, TimerAbsolute, wakeUpTime, 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->proto_ops, + &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; + int delay = 0; + Bool newDelay = FALSE; + + while (SynapticsGetHwState(pInfo, priv, &hw)) { + hw.millis = GetTimeInMillis(); + priv->hwState = hw; + delay = HandleState(pInfo, &hw); + newDelay = TRUE; + } + + if (newDelay) + priv->timer = TimerSet(priv->timer, 0, delay, timerFunc, pInfo); +} + +static int +HandleMidButtonEmulation(SynapticsPrivate *priv, struct SynapticsHwState *hw, int *delay) +{ + SynapticsParameters *para = &priv->synpara; + Bool done = FALSE; + int timeleft; + int mid = 0; + + while (!done) { + switch (priv->mid_emu_state) { + case MBE_LEFT_CLICK: + case MBE_RIGHT_CLICK: + case MBE_OFF: + priv->button_delay_millis = hw->millis; + 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, + hw->millis); + 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, + hw->millis); + 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_press && priv->finger_state < FS_PRESSED) + finger = FS_PRESSED; + else if (hw->z > para->finger_high && priv->finger_state < FS_TOUCHED) + finger = FS_TOUCHED; + else if (hw->z < para->finger_low && priv->finger_state > FS_UNTOUCHED) + finger = FS_UNTOUCHED; + else + finger = priv->finger_state; + + if (!para->palm_detect) + return finger; + + /* palm detection */ + if (finger) { + if ((hw->z > para->palm_min_z) && (hw->fingerWidth > para->palm_min_width)) + priv->palm = TRUE; + } else { + priv->palm = FALSE; + } + if (hw->x == 0) + priv->avg_width = 0; + else + priv->avg_width += (hw->fingerWidth - priv->avg_width + 1) / 2; + if (finger && !priv->finger_state) { + 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; + + if (priv->palm) + finger = FS_UNTOUCHED; + + 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: + default: + 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; + } + + 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, int millis) +{ + SynapticsParameters *para = &priv->synpara; + DBG(7, "SetTapState - %d -> %d (millis:%d)\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: + if (para->tap_and_drag_gesture) + priv->tap_button_state = TBS_BUTTON_DOWN; + else + priv->tap_button_state = TBS_BUTTON_UP; + 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, int millis) +{ + DBG(7, "SetMovingState - %d -> %d center at %d/%d (millis:%d)\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, + enum FingerState finger, Bool inside_active_area) +{ + SynapticsParameters *para = &priv->synpara; + Bool touch, release, is_timeout, move; + int timeleft, timeout; + edge_type edge; + int delay = 1000000000; + + if (priv->palm) + return delay; + + touch = finger && !priv->finger_state; + release = !finger && priv->finger_state; + move = (finger && + (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))); + + if (touch) { + priv->touch_on.x = hw->x; + priv->touch_on.y = hw->y; + priv->touch_on.millis = hw->millis; + } else if (release) { + priv->touch_on.millis = hw->millis; + } + 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, hw->millis); + is_timeout = timeleft <= 0; + + restart: + switch (priv->tap_state) { + case TS_START: + if (touch) + SetTapState(priv, TS_1, hw->millis); + break; + case TS_1: + if (move) { + SetMovingState(priv, MS_TOUCHPAD_RELATIVE, hw->millis); + SetTapState(priv, TS_MOVE, hw->millis); + goto restart; + } else if (is_timeout) { + if (finger == FS_TOUCHED) { + SetMovingState(priv, MS_TOUCHPAD_RELATIVE, hw->millis); + } else if (finger == FS_PRESSED) { + SetMovingState(priv, MS_TRACKSTICK, hw->millis); + } + SetTapState(priv, TS_MOVE, hw->millis); + 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, hw->millis); + } + break; + case TS_MOVE: + if (move && priv->moving_state == MS_TRACKSTICK) { + SetMovingState(priv, MS_TOUCHPAD_RELATIVE, hw->millis); + } + if (release) { + SetMovingState(priv, MS_FALSE, hw->millis); + SetTapState(priv, TS_START, hw->millis); + } + break; + case TS_2A: + if (touch) + SetTapState(priv, TS_3, hw->millis); + else if (is_timeout) + SetTapState(priv, TS_SINGLETAP, hw->millis); + break; + case TS_2B: + if (touch) { + SetTapState(priv, TS_3, hw->millis); + } else if (is_timeout) { + SetTapState(priv, TS_START, hw->millis); + priv->tap_button_state = TBS_BUTTON_DOWN_UP; + } + break; + case TS_SINGLETAP: + if (touch) + SetTapState(priv, TS_1, hw->millis); + else if (is_timeout) + SetTapState(priv, TS_START, hw->millis); + break; + case TS_3: + if (move) { + if (para->tap_and_drag_gesture) { + SetMovingState(priv, MS_TOUCHPAD_RELATIVE, hw->millis); + SetTapState(priv, TS_DRAG, hw->millis); + } else { + SetTapState(priv, TS_1, hw->millis); + } + goto restart; + } else if (is_timeout) { + if (para->tap_and_drag_gesture) { + if (finger == FS_TOUCHED) { + SetMovingState(priv, MS_TOUCHPAD_RELATIVE, hw->millis); + } else if (finger == FS_PRESSED) { + SetMovingState(priv, MS_TRACKSTICK, hw->millis); + } + SetTapState(priv, TS_DRAG, hw->millis); + } else { + SetTapState(priv, TS_1, hw->millis); + } + goto restart; + } else if (release) { + SetTapState(priv, TS_2B, hw->millis); + } + break; + case TS_DRAG: + if (move) + SetMovingState(priv, MS_TOUCHPAD_RELATIVE, hw->millis); + if (release) { + SetMovingState(priv, MS_FALSE, hw->millis); + if (para->locked_drags) { + SetTapState(priv, TS_4, hw->millis); + } else { + SetTapState(priv, TS_START, hw->millis); + } + } + break; + case TS_4: + if (is_timeout) { + SetTapState(priv, TS_START, hw->millis); + goto restart; + } + if (touch) + SetTapState(priv, TS_5, hw->millis); + break; + case TS_5: + if (is_timeout || move) { + SetTapState(priv, TS_DRAG, hw->millis); + goto restart; + } else if (release) { + SetMovingState(priv, MS_FALSE, hw->millis); + SetTapState(priv, TS_START, hw->millis); + } + break; + } + + timeout = GetTimeOut(priv); + if (timeout >= 0) { + timeleft = TIME_DIFF(priv->touch_on.millis + timeout, hw->millis); + delay = clamp(timeleft, 1, delay); + } + return delay; +} + +#define HIST(a) (priv->move_hist[((priv->hist_index - (a) + SYNAPTICS_MOVE_HISTORY) % SYNAPTICS_MOVE_HISTORY)]) + +static void +store_history(SynapticsPrivate *priv, int x, int y, unsigned int 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; +} + +/* + * 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; + + /* HIST is full enough: priv->count_packet_finger > 3 */ + *dx = estimate_delta(hw->x, HIST(0).x, HIST(1).x, HIST(2).x); + *dy = estimate_delta(hw->y, HIST(0).y, HIST(1).y, HIST(2).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. */ + 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: + if (hw->numFingers == 1) + moving_state = MS_TOUCHPAD_RELATIVE; + break; + default: + break; + } + } + + if (!inside_area || !moving_state || priv->palm || + 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) + { + /* reset packet counter. */ + priv->count_packet_finger = 0; + goto out; + } + + /* to create fluid edge motion, call back 'soon' + * even in the absence of new hardware events */ + delay = MIN(delay, 13); + + if (priv->count_packet_finger <= 3) /* min. 3 packets, see get_delta() */ + goto skip; /* 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); + +skip: + priv->count_packet_finger++; +out: + priv->prevFingers = hw->numFingers; + + *dxP = dx; + *dyP = dy; + + return delay; +} + +struct ScrollData { + int left, right, up, down; +}; + +static void +start_coasting(SynapticsPrivate *priv, struct SynapticsHwState *hw, edge_type edge, + Bool vertical) +{ + SynapticsParameters *para = &priv->synpara; + + priv->autoscroll_y = 0.0; + priv->autoscroll_x = 0.0; + + if ((priv->scroll_packet_count > 3) && (para->coasting_speed > 0.0)) { + double pkt_time = (HIST(0).millis - HIST(3).millis) / 1000.0; + if (para->scroll_twofinger_vert || vertical) { + double dy = estimate_delta(HIST(0).y, HIST(1).y, HIST(2).y, HIST(3).y); + int sdelta = para->scroll_dist_vert; + if ((para->scroll_twofinger_vert || (edge & RIGHT_EDGE)) && pkt_time > 0 && sdelta > 0) { + double scrolls_per_sec = dy / pkt_time / sdelta; + if (fabs(scrolls_per_sec) >= para->coasting_speed) { + priv->autoscroll_yspd = scrolls_per_sec; + priv->autoscroll_y = (hw->y - priv->scroll_y) / (double)sdelta; + } + } + } + if (para->scroll_twofinger_horiz || !vertical){ + double dx = estimate_delta(HIST(0).x, HIST(1).x, HIST(2).x, HIST(3).x); + int sdelta = para->scroll_dist_horiz; + if ((para->scroll_twofinger_horiz || (edge & BOTTOM_EDGE)) && pkt_time > 0 && sdelta > 0) { + double scrolls_per_sec = dx / pkt_time / sdelta; + if (fabs(scrolls_per_sec) >= para->coasting_speed) { + priv->autoscroll_xspd = scrolls_per_sec; + priv->autoscroll_x = (hw->x - priv->scroll_x) / (double)sdelta; + } + } + } + } + priv->scroll_packet_count = 0; +} + +static void +stop_coasting(SynapticsPrivate *priv) +{ + priv->autoscroll_xspd = 0; + priv->autoscroll_yspd = 0; + priv->scroll_packet_count = 0; +} + +static int +HandleScrolling(SynapticsPrivate *priv, struct SynapticsHwState *hw, + edge_type edge, Bool finger, struct ScrollData *sd) +{ + SynapticsParameters *para = &priv->synpara; + int delay = 1000000000; + + sd->left = sd->right = sd->up = sd->down = 0; + + if (priv->synpara.touchpad_off == 2) { + 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) { + stop_coasting(priv); + 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_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)) { + priv->vert_scroll_twofinger_on = TRUE; + priv->vert_scroll_edge_on = FALSE; + priv->scroll_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)) { + priv->horiz_scroll_twofinger_on = TRUE; + priv->horiz_scroll_edge_on = FALSE; + priv->scroll_x = hw->x; + DBG(7, "horiz two-finger scroll detected\n"); + } + } + } + if (finger && !priv->finger_state) { + 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_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_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); + 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->autoscroll_xspd || priv->autoscroll_yspd)) { + 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 */ + if ((oldv || oldh) && !para->scroll_edge_corner && + !(priv->circ_scroll_on || priv->vert_scroll_edge_on || + priv->horiz_scroll_edge_on || priv->horiz_scroll_twofinger_on || + priv->vert_scroll_twofinger_on)) { + start_coasting(priv, hw, edge, oldv); + } + } + + /* 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->autoscroll_yspd == 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, edge, TRUE); + } + } else if (para->circular_scrolling) { + priv->vert_scroll_edge_on = FALSE; + priv->circ_scroll_on = TRUE; + priv->circ_scroll_vert = TRUE; + priv->scroll_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->autoscroll_xspd == 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, edge, FALSE); + } + } else if (para->circular_scrolling) { + priv->horiz_scroll_edge_on = FALSE; + priv->circ_scroll_on = TRUE; + priv->circ_scroll_vert = FALSE; + priv->scroll_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_packet_count++; + } + + if (priv->vert_scroll_edge_on || priv->vert_scroll_twofinger_on) { + /* + = down, - = up */ + int delta = para->scroll_dist_vert; + if (delta > 0) { + while (hw->y - priv->scroll_y > delta) { + sd->down++; + priv->scroll_y += delta; + } + while (hw->y - priv->scroll_y < -delta) { + sd->up++; + priv->scroll_y -= delta; + } + } + } + if (priv->horiz_scroll_edge_on || priv->horiz_scroll_twofinger_on) { + /* + = right, - = left */ + int delta = para->scroll_dist_horiz; + if (delta > 0) { + while (hw->x - priv->scroll_x > delta) { + sd->right++; + priv->scroll_x += delta; + } + while (hw->x - priv->scroll_x < -delta) { + sd->left++; + priv->scroll_x -= delta; + } + } + } + if (priv->circ_scroll_on) { + /* + = counter clockwise, - = clockwise */ + double delta = para->scroll_dist_circ; + if (delta >= 0.005) { + while (diffa(priv->scroll_a, angle(priv, hw->x, hw->y)) > delta) { + if (priv->circ_scroll_vert) + sd->up++; + else + sd->right++; + priv->scroll_a += delta; + if (priv->scroll_a > M_PI) + priv->scroll_a -= 2 * M_PI; + } + while (diffa(priv->scroll_a, angle(priv, hw->x, hw->y)) < -delta) { + if (priv->circ_scroll_vert) + sd->down++; + else + sd->left++; + priv->scroll_a -= delta; + if (priv->scroll_a < -M_PI) + priv->scroll_a += 2 * M_PI; + } + } + } + + if (priv->autoscroll_yspd) { + double dtime = (hw->millis - HIST(0).millis) / 1000.0; + double ddy = para->coasting_friction * dtime; + priv->autoscroll_y += priv->autoscroll_yspd * dtime; + delay = MIN(delay, 20); + while (priv->autoscroll_y > 1.0) { + sd->down++; + priv->autoscroll_y -= 1.0; + } + while (priv->autoscroll_y < -1.0) { + sd->up++; + priv->autoscroll_y += 1.0; + } + if (abs(priv->autoscroll_yspd) < ddy) { + priv->autoscroll_yspd = 0; + priv->scroll_packet_count = 0; + } else { + priv->autoscroll_yspd += (priv->autoscroll_yspd < 0 ? ddy : -1*ddy); + } + } + + if (priv->autoscroll_xspd) { + double dtime = (hw->millis - HIST(0).millis) / 1000.0; + double ddx = para->coasting_friction * dtime; + priv->autoscroll_x += priv->autoscroll_xspd * dtime; + delay = MIN(delay, 20); + while (priv->autoscroll_x > 1.0) { + sd->right++; + priv->autoscroll_x -= 1.0; + } + while (priv->autoscroll_x < -1.0) { + sd->left++; + priv->autoscroll_x += 1.0; + } + if (abs(priv->autoscroll_xspd) < ddx) { + priv->autoscroll_xspd = 0; + priv->scroll_packet_count = 0; + } else { + priv->autoscroll_xspd += (priv->autoscroll_xspd < 0 ? ddx : -1*ddx); + } + } + + return delay; +} + +static void +handle_clickfinger(SynapticsParameters *para, struct SynapticsHwState *hw) +{ + int action = 0; + switch(hw->numFingers){ + 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; + break; + case 2: + hw->left = 0; + hw->middle = 1; + break; + case 3: + hw->left = 0; + hw->right = 1; + 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, 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, delay); + + /* Fingers emulate other buttons */ + if(hw->left && hw->numFingers >= 1){ + handle_clickfinger(para, 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, struct ScrollData scroll) +{ + while (scroll.up-- > 0) + post_button_click(pInfo, 4); + + while (scroll.down-- > 0) + post_button_click(pInfo, 5); + + while (scroll.left-- > 0) + post_button_click(pInfo, 6); + + while (scroll.right-- > 0) + post_button_click(pInfo, 7); +} + +static inline int +repeat_scrollbuttons(const InputInfoPtr pInfo, + const struct SynapticsHwState *hw, + int buttons, int delay) +{ + SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private); + SynapticsParameters *para = &priv->synpara; + int repeat_delay, timeleft; + int rep_buttons = ((para->updown_button_repeat ? 0x18 : 0) | + (para->leftright_button_repeat ? 0x60 : 0)); + + /* 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 = hw->millis + repeat_delay * 2; + } + } else { + priv->repeatButtons = 0; + priv->nextRepeat = 0; + } + + if (priv->repeatButtons) { + timeleft = TIME_DIFF(priv->nextRepeat, hw->millis); + 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)); + xf86PostButtonEvent(pInfo->dev, FALSE, id, FALSE, 0, 0); + xf86PostButtonEvent(pInfo->dev, FALSE, id, TRUE, 0, 0); + } + + priv->nextRepeat = hw->millis + repeat_delay; + delay = MIN(delay, repeat_delay); + } + } + + return delay; +} + +/* + * 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. + */ +static int +HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw) +{ + SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private); + SynapticsParameters *para = &priv->synpara; + int finger; + int dx, dy, buttons, id; + edge_type edge = NO_EDGE; + int change; + struct ScrollData scroll; + 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) + return delay; + + /* 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. */ + priv->hyst_center_x = hysteresis(hw->x, priv->hyst_center_x, para->hyst_x); + priv->hyst_center_y = hysteresis(hw->y, priv->hyst_center_y, para->hyst_y); + hw->x = priv->hyst_center_x; + hw->y = priv->hyst_center_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) + { + hw->x = 0; + hw->y = 0; + hw->z = 0; + hw->numFingers = 0; + hw->fingerWidth = 0; + + /* FIXME: if finger accidentally moves into the area and doesn't + * really release, the finger should remain down. */ + finger = FS_UNTOUCHED; + edge = NO_EDGE; + + dx = dy = 0; + } + + /* these two just update hw->left, right, etc. */ + update_hw_button_state(pInfo, hw, &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); + finger = SynapticsDetectFinger(priv, hw); + } + + /* 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, 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, &scroll); + 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_UNTOUCHED) { + 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); + } + + /* Process scroll events only if coordinates are + * in the Synaptics Area + */ + if (inside_active_area) + post_scroll_events(pInfo, scroll); + + if (double_click) { + post_button_click(pInfo, 1); + post_button_click(pInfo, 1); + } + + if (priv->has_scrollbuttons) + delay = repeat_scrollbuttons(pInfo, hw, buttons, delay); + + /* 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); + + 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); +} + +static Bool +QueryHardware(InputInfoPtr pInfo) +{ + SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private; + + priv->comm.protoBufTail = 0; + + if (!priv->proto_ops->QueryHardware(pInfo)) { + xf86Msg(X_PROBED, "%s: no supported touchpad found\n", pInfo->name); + 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/driver/xf86-input-synaptics/src/synapticsstr.h b/driver/xf86-input-synaptics/src/synapticsstr.h new file mode 100644 index 000000000..066b3f379 --- /dev/null +++ b/driver/xf86-input-synaptics/src/synapticsstr.h @@ -0,0 +1,249 @@ +/* + * 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" + +#ifdef DBG +# undef DBG +#endif + +#ifdef DEBUG +#define DBG(verb, ...) \ + xf86MsgVerb(X_INFO, verb, __VA_ARGS__) +#else +#define DBG(verb, msg, ...) /* */ +#endif + +/****************************************************************************** + * Definitions + * structs, typedefs, #defines, enums + *****************************************************************************/ +#define SYNAPTICS_MOVE_HISTORY 5 + +typedef struct _SynapticsMoveHist +{ + int x, y; + int millis; +} SynapticsMoveHistRec; + +enum FingerState { /* Note! The order matters. Compared with < operator. */ + FS_UNTOUCHED, + FS_TOUCHED, + FS_PRESSED +}; + +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 */ +}; + +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 +}; + +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 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, egde 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 */ + 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 hyst_x, hyst_y; /* x and y width of hysteresis box */ +} SynapticsParameters; + + +typedef 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; + + Bool shm_config; /* True when shared memory area allocated */ + + OsTimerPtr timer; /* for up/down-button repeat, tap processing, etc */ + + struct CommData comm; + + 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*/ + int scroll_y; /* last y-scroll position */ + int scroll_x; /* last x-scroll position */ + double scroll_a; /* last angle-scroll position */ + 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 */ + + 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 autoscroll_xspd; /* Horizontal coasting speed */ + double autoscroll_yspd; /* Vertical coasting speed */ + double autoscroll_x; /* Accumulated horizontal coasting scroll */ + double autoscroll_y; /* Accumulated vertical coasting scroll */ + int scroll_packet_count; /* Scroll duration */ + 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 palm; /* Set to true when palm detected, reset to false when + palm/finger contact disappears */ + 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 */ + + enum TouchpadModel model; /* The detected model */ +} SynapticsPrivate; + + +extern void SynapticsDefaultDimensions(InputInfoPtr pInfo); + +#endif /* _SYNAPTICSSTR_H_ */ diff --git a/driver/xf86-input-synaptics/src/synproto.h b/driver/xf86-input-synaptics/src/synproto.h new file mode 100644 index 000000000..96ddf3e92 --- /dev/null +++ b/driver/xf86-input-synaptics/src/synproto.h @@ -0,0 +1,106 @@ +/* + * 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> + +/* + * A structure to describe the state of the touchpad hardware (buttons and pad) + */ +struct SynapticsHwState { + int millis; /* Timestamp in milliseconds */ + int x; /* X position of finger */ + int y; /* Y position of finger */ + int z; /* Finger pressure */ + int numFingers; + int fingerWidth; + + Bool left; + Bool right; + Bool up; + Bool down; + + Bool multi[8]; + Bool middle; /* Some ALPS touchpads have a middle button */ +}; + +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; +}; + +enum SynapticsProtocol { + SYN_PROTO_PSAUX, /* Raw psaux device */ +#ifdef BUILD_EVENTCOMM + SYN_PROTO_EVENT, /* Linux kernel event interface */ +#endif /* BUILD_EVENTCOMM */ +#ifdef BUILD_PSMCOMM + SYN_PROTO_PSM, /* FreeBSD psm driver */ +#endif /* BUILD_PSMCOMM */ + SYN_PROTO_ALPS /* ALPS touchpad protocol */ +}; + +struct _SynapticsParameters; +struct SynapticsHwInfo; +struct CommData; + +struct SynapticsProtocolOperations { + void (*DeviceOnHook)(InputInfoPtr pInfo, struct _SynapticsParameters *para); + void (*DeviceOffHook)(InputInfoPtr pInfo); + Bool (*QueryHardware)(InputInfoPtr pInfo); + Bool (*ReadHwState)(InputInfoPtr pInfo, + struct SynapticsProtocolOperations *proto_ops, + struct CommData *comm, struct SynapticsHwState *hwRet); + Bool (*AutoDevProbe)(InputInfoPtr pInfo); + void (*ReadDevDimensions)(InputInfoPtr pInfo); +}; + +extern struct SynapticsProtocolOperations psaux_proto_operations; +#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 SynapticsProtocolOperations alps_proto_operations; + + +#endif /* _SYNPROTO_H_ */ |