summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am9
-rw-r--r--README33
-rw-r--r--conf/50-wacom.conf29
-rw-r--r--conf/Makefile.am (renamed from fdi/Makefile.am)5
-rw-r--r--conf/wacom.fdi (renamed from fdi/wacom.fdi)3
-rw-r--r--configure.ac159
-rw-r--r--doc/.gitignore2
-rw-r--r--doc/Makefile.am14
-rw-r--r--doc/doxygen.conf.in1551
-rw-r--r--doc/footer.html4
-rw-r--r--include/Makefile.am3
-rw-r--r--include/Xwacom.h17
-rw-r--r--include/isdv4.h221
-rw-r--r--include/wacom-properties.h44
-rw-r--r--include/wacom-util.h51
-rw-r--r--man/Makefile.am29
-rw-r--r--man/wacom.man193
-rw-r--r--man/xsetwacom.man150
-rwxr-xr-xrelease.sh227
-rw-r--r--src/Makefile.am12
-rw-r--r--src/common.mk16
-rw-r--r--src/wcmCommon.c2428
-rw-r--r--src/wcmConfig.c378
-rw-r--r--src/wcmFilter.c242
-rw-r--r--src/wcmFilter.h3
-rw-r--r--src/wcmISDV4.c859
-rw-r--r--src/wcmISDV4.h88
-rw-r--r--src/wcmTouchFilter.c657
-rw-r--r--src/wcmTouchFilter.h31
-rw-r--r--src/wcmUSB.c1861
-rw-r--r--src/wcmValidateDevice.c788
-rw-r--r--src/wcmXCommand.c1023
-rw-r--r--src/xf86Wacom.c1173
-rw-r--r--src/xf86Wacom.h76
-rw-r--r--src/xf86WacomDefs.h292
-rw-r--r--test/Makefile.am19
-rw-r--r--test/fake-symbols.c498
-rw-r--r--test/fake-symbols.h193
-rw-r--r--test/wacom-tests.c642
-rw-r--r--tools/.gitignore1
-rw-r--r--tools/Makefile.am16
-rw-r--r--tools/isdv4-serial-debugger.c589
-rw-r--r--tools/xsetwacom.c2541
43 files changed, 11303 insertions, 5867 deletions
diff --git a/Makefile.am b/Makefile.am
index 87eb702..2293d88 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,12 +18,11 @@
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-AUTOMAKE_OPTIONS = foreign
+# Provide an sdk location that is known to be writable
+DISTCHECK_CONFIGURE_FLAGS = --with-sdkdir='$${includedir}/xorg' \
+ --with-xorg-conf-dir='$${datadir}/X11/xorg.conf.d'
-# Ensure headers are installed below $(prefix) for distcheck
-DISTCHECK_CONFIGURE_FLAGS = --with-sdkdir='$${includedir}/xorg'
-
-SUBDIRS = fdi src man include tools
+SUBDIRS = conf doc src man include tools test
MAINTAINERCLEANFILES = ChangeLog INSTALL
pkgconfigdir = $(libdir)/pkgconfig
diff --git a/README b/README
index aaef526..ec7f5e3 100644
--- a/README
+++ b/README
@@ -3,14 +3,8 @@ It obsoletes the linuxwacom driver and supports X server versions 1.7 and
higher. Server versions lower than 1.7 may be supported by this driver, but
users are encouraged to use the old linuxwacom driver instead.
-Provided that hotplugging is enabled in the X server, the simplest
-configuration is to copy the wacom.fdi file into /etc/hal/fdi/policies and
-to restart HAL and then plug in the tablet. The driver will autoconfigure
-itself.
-
-If hotplugging is not possible or desired, an InputDevice section in the
-xorg.conf with the Driver wacom will load the device. Details on the
-configuration can be found in xorg.conf(5) and wacom(4).
+Information about building this driver, configuration and general use is
+available on http://linuxwacom.sourceforge.net
Since this driver is an X11 driver only, a kernel driver is required to get
hardware support. If the kernel driver is older than this driver, some
@@ -20,23 +14,34 @@ features may not be available.
BUILDING FROM GIT:
$ ./autogen.sh --prefix=/usr
+ $ make
+
Adjust the prefix according to your local setup, in most cases, a prefix of
/usr is correct.
BUILDING FROM TARBALL:
- $ ./configure && make
+ $ ./configure --prefix=/usr
+ $ make
+
Adjust the prefix according to your local setup, in most cases, a prefix of
/usr is correct.
==============================================================================
-Please surf the HOWTO page at http://linuxwacom.sf.net/index.php/howto/main
-(or the miniHOWTO at http://linuxwacom.sourceforge.net/index.php/minihowto if
-you run a Fedora system) to find the proper options if the default building
-environment doesn't work for you.
+Please surf the HOWTO pages at
+http://sourceforge.net/apps/mediawiki/linuxwacom/index.php?title=Category:HOWTO
+to find the proper options if the default building environment doesn't work
+for you.
==============================================================================
-WEBSITE: http://linuxwacom.sf.net
+Developers, please surf the DeveloperPages at
+http://sourceforge.net/apps/mediawiki/linuxwacom/index.php?title=Category:DeveloperPages
+to find more information on how to contribute to this driver.
+
+==============================================================================
+
+
+WEBSITE: http://linuxwacom.sourceforge.net
diff --git a/conf/50-wacom.conf b/conf/50-wacom.conf
new file mode 100644
index 0000000..2c9ccb6
--- /dev/null
+++ b/conf/50-wacom.conf
@@ -0,0 +1,29 @@
+Section "InputClass"
+ Identifier "Wacom class"
+ MatchProduct "Wacom|WACOM|WALTOP|Hanwang"
+ MatchDevicePath "/dev/input/event*"
+ Driver "wacom"
+EndSection
+
+Section "InputClass"
+ Identifier "Wacom serial class"
+ MatchProduct "Serial Wacom Tablet"
+ Driver "wacom"
+EndSection
+
+Section "InputClass"
+ Identifier "Wacom serial class identifiers"
+ MatchProduct "WACf|FUJ02e5|FUJ02e7|FUJ02e9"
+ Driver "wacom"
+EndSection
+
+
+# N-Trig Duosense Electromagnetic Digitizer
+Section "InputClass"
+ Identifier "Wacom N-Trig class"
+ MatchProduct "HID 1b96:0001|N-Trig Pen"
+ MatchDevicePath "/dev/input/event*"
+ Driver "wacom"
+ Option "Button2" "3"
+EndSection
+
diff --git a/fdi/Makefile.am b/conf/Makefile.am
index cafb94a..ab138f5 100644
--- a/fdi/Makefile.am
+++ b/conf/Makefile.am
@@ -1,3 +1,6 @@
-EXTRA_DIST = wacom.fdi
+if HAS_XORG_CONF_DIR
+dist_config_DATA = 50-wacom.conf
+else
fdidir = $(datadir)/hal/fdi/policy/20thirdparty
dist_fdi_DATA = wacom.fdi
+endif
diff --git a/fdi/wacom.fdi b/conf/wacom.fdi
index 5c71563..f35d6a5 100644
--- a/fdi/wacom.fdi
+++ b/conf/wacom.fdi
@@ -8,10 +8,9 @@
</match>
</match>
<match key="info.capabilities" contains="serial">
- <match key="@info.parent:pnp.id" contains_outof="WACf;FUJ02e5;FUJ02e7">
+ <match key="@info.parent:pnp.id" contains_outof="WACf;FUJ02e5;FUJ02e7;FUJ02e9">
<append key="info.capabilities" type="strlist">input</append>
<merge key="input.x11_driver" type="string">wacom</merge>
- <merge key="input.x11_options.ForceDevice" type="string">ISDV4</merge>
<merge key="input.device" type="copy_property">serial.device</merge>
</match>
</match>
diff --git a/configure.ac b/configure.ac
index f355320..ee9508f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,77 +20,142 @@
#
# Process this file with autoconf to produce a configure script
-AC_PREREQ(2.57)
+# Initialize Autoconf
+AC_PREREQ([2.60])
AC_INIT([xf86-input-wacom],
- 0.10.5,
+ [0.13.0],
[https://bugs.freedesktop.org/enter_bug.cgi?product=xorg],
- xf86-input-wacom)
-
+ [xf86-input-wacom])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([Makefile.am])
-AC_CONFIG_AUX_DIR(.)
-AM_INIT_AUTOMAKE([dist-bzip2])
+AC_CONFIG_HEADERS([config.h])
+# Initialize Automake
+AM_INIT_AUTOMAKE([foreign dist-bzip2])
AM_MAINTAINER_MODE
+AC_USE_SYSTEM_EXTENSIONS
-# Require xorg-macros: XORG_DEFAULT_OPTIONS
-m4_ifndef([XORG_MACROS_VERSION], [AC_FATAL([must install xorg-macros 1.3 or later before running autoconf/autogen])])
-XORG_MACROS_VERSION(1.3)
-AM_CONFIG_HEADER([config.h])
-
-# Checks for programs.
+# Initialize libtool
AC_DISABLE_STATIC
AC_PROG_LIBTOOL
-AC_PROG_CC
+
+# Initialize X.Org macros 1.8 or later for MAN_SUBSTS set by XORG_MANPAGE_SECTIONS
+m4_ifndef([XORG_MACROS_VERSION],
+ [m4_fatal([must install xorg-macros 1.8 or later before running autoconf/autogen])])
+XORG_MACROS_VERSION([1.8])
XORG_DEFAULT_OPTIONS
+XORG_WITH_DOXYGEN(1.6.1)
+
+# Checks for libraries.
+AC_CHECK_LIB([m], [rint])
+
+# Obtain compiler/linker options from server and required extensions
+PKG_CHECK_MODULES(XORG, [xorg-server >= 1.7.0] xproto xext kbproto inputproto randrproto)
+
+# Obtain compiler/linker options for the xsetwacom tool
+PKG_CHECK_MODULES(X11, x11 xi xrandr xinerama)
-AH_TOP([
-#ifndef WACOM_TOOLS
-#include "xorg-server.h"
-#endif])
+# Obtain compiler/linker options for libudev used by ISDV4 code
+PKG_CHECK_MODULES(UDEV, libudev)
+
+# X Server SDK location is required to install wacom header files
+# This location is also relayed in the xorg-wacom.pc file
+sdkdir=`$PKG_CONFIG --variable=sdkdir xorg-server`
+
+# Workaround overriding sdkdir to be able to create a tarball when user has no
+# write permission in sdkdir. See DISTCHECK_CONFIGURE_FLAGS in Makefile.am
+AC_ARG_WITH([sdkdir], [], [sdkdir="$withval"])
+AC_SUBST([sdkdir])
+
+DRIVER_NAME=wacom
+AC_SUBST([DRIVER_NAME])
-AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],
- [Enable debugging (default: disabled)]),
- [DEBUGGING=$enableval], [DEBUGGING=no])
+# -----------------------------------------------------------------------------
+# Configuration options
+# -----------------------------------------------------------------------------
+# Define a configure option for code debugging
+AC_ARG_ENABLE(debug,
+ AS_HELP_STRING([--enable-debug],
+ [Enable debugging (default: disabled)]),
+ [DEBUGGING=$enableval], [DEBUGGING=yes])
+# Define the C preprocessor macro DEBUG in config.h
+if test "x$DEBUGGING" = xyes; then
+ AC_DEFINE(DEBUG, 1, [Enable debugging code])
+fi
+# Define a configure option for an alternate input module directory
AC_ARG_WITH(xorg-module-dir,
- AC_HELP_STRING([--with-xorg-module-dir=DIR],
+ AS_HELP_STRING([--with-xorg-module-dir=DIR],
[Default xorg module directory [[default=$libdir/xorg/modules]]]),
[moduledir="$withval"],
[moduledir="$libdir/xorg/modules"])
inputdir=${moduledir}/input
AC_SUBST(inputdir)
-if test "x$DEBUGGING" = xyes; then
- AC_DEFINE(DEBUG, 1, [Enable debugging code])
+# Define a configure option for an alternate X Server configuration directory
+sysconfigdir=`$PKG_CONFIG --variable=sysconfigdir xorg-server`
+AC_ARG_WITH([xorg-conf-dir],
+ AS_HELP_STRING([--with-xorg-conf-dir=DIR],
+ [Default xorg.conf.d directory [[default=from $PKG_CONFIG xorg-server]]]),
+ [configdir="$withval"],
+ [configdir="$sysconfigdir"])
+AC_SUBST(configdir)
+AM_CONDITIONAL(HAS_XORG_CONF_DIR, [test "x$sysconfigdir" != "x"])
+
+AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--enable-unit-tests],
+ [Enable unit-tests (default: auto)]),
+ [UNITTESTS=$enableval],
+ [UNITTESTS=auto])
+
+# If unittests aren't explicitly disabled, check for required support
+if test "x$UNITTESTS" != xno ; then
+ # Check if linker supports -wrap, passed via compiler flags
+ # When cross-compiling, reports no, since unit tests run from
+ # "make check", so would be running on build machine, not target
+ AC_MSG_CHECKING([whether the linker supports -wrap])
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -Wl,-wrap,exit"
+ AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+ void __wrap_exit (int s)
+ {
+ __real_exit (0);
+ }]],
+ [[exit (1);]])],
+ [linker_can_wrap="yes"],
+ [linker_can_wrap="no"],
+ [linker_can_wrap="no"])
+ AC_MSG_RESULT([$linker_can_wrap])
+ LDFLAGS="$save_LDFLAGS"
fi
-AM_CONDITIONAL(DEBUG, [test "x$DEBUGGING" = xyes])
-
-# Checks for extensions
-XORG_DRIVER_CHECK_EXT(XINPUT, inputproto)
-# Checks for pkg-config packages. We need to be able to override sdkdir
-# to satisfy silly distcheck requirements.
-PKG_CHECK_MODULES(XORG, xorg-server xproto $REQUIRED_MODULES)
-
-AC_ARG_WITH([sdkdir], [],
- [sdkdir="$withval"],
- [sdkdir=`$PKG_CONFIG --variable=sdkdir xorg-server`])
-AC_SUBST([sdkdir])
+if test "x$UNITTESTS" = xauto; then
+ if test "x$linker_can_wrap" = xyes; then
+ UNITTESTS=yes
+ else
+ UNITTESTS=no
+ fi
+fi
+if test "x$UNITTESTS" = xyes; then
+ if test "x$linker_can_wrap" = xno; then
+ AC_MSG_ERROR([ld -wrap support required to build unit tests])
+ fi
+ AC_DEFINE(UNITTESTS, 1, [Enable unit tests])
+fi
+AM_CONDITIONAL(UNITTESTS, [test "x$UNITTESTS" = xyes])
-PKG_CHECK_MODULES(X11, x11 xi)
-# Checks for header files.
-AC_HEADER_STDC
-DRIVER_NAME=wacom
-AC_SUBST([DRIVER_NAME])
+# -----------------------------------------------------------------------------
-AC_OUTPUT([Makefile
- fdi/Makefile
- src/Makefile
- man/Makefile
- include/Makefile
- tools/Makefile
- xorg-wacom.pc])
+AC_CONFIG_FILES([Makefile
+ conf/Makefile
+ doc/Makefile
+ doc/doxygen.conf
+ src/Makefile
+ man/Makefile
+ include/Makefile
+ tools/Makefile
+ test/Makefile
+ xorg-wacom.pc])
+AC_OUTPUT
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000..550262f
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,2 @@
+html/
+doxygen.conf
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..5dec7bb
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,14 @@
+
+if HAVE_DOXYGEN
+
+# Developers documentation is not installed
+noinst_DATA = html/index.html
+dist_noinst_DATA = doxygen.conf.in footer.html
+
+html/index.html:
+ $(AM_V_GEN)$(DOXYGEN) doxygen.conf
+
+clean-local:
+ $(AM_V_at)rm -fr html/
+
+endif HAVE_DOXYGEN
diff --git a/doc/doxygen.conf.in b/doc/doxygen.conf.in
new file mode 100644
index 0000000..6b5f7c1
--- /dev/null
+++ b/doc/doxygen.conf.in
@@ -0,0 +1,1551 @@
+# Doxyfile 1.6.2
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = "X.Org Wacom Input Driver"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = "@PACKAGE_VERSION@"
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = @abs_builddir@
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = NO
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = ../src ../include
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER = @abs_srcdir@/footer.html
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/doc/footer.html b/doc/footer.html
new file mode 100644
index 0000000..f80dc82
--- /dev/null
+++ b/doc/footer.html
@@ -0,0 +1,4 @@
+<hr size="1"/><address style="text-align: right;"><small>
+Generated on $datetime for $projectname by&nbsp;<a href="http://www.doxygen.org/index.html"><img class="footer" src="doxygen.png" alt="doxygen"/></a> $doxygenversion</small></address>
+</body>
+</html>
diff --git a/include/Makefile.am b/include/Makefile.am
index ccff26d..a61df73 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,2 +1 @@
-EXTRA_DIST = Xwacom.h wacom-properties.h
-sdk_HEADERS = Xwacom.h wacom-properties.h
+sdk_HEADERS = Xwacom.h wacom-properties.h isdv4.h wacom-util.h
diff --git a/include/Xwacom.h b/include/Xwacom.h
index f0fdd64..3ca8573 100644
--- a/include/Xwacom.h
+++ b/include/Xwacom.h
@@ -17,6 +17,15 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+/**
+ * @mainpage The X.Org Wacom Input Driver API Documentation
+ * @section intro Introduction
+ * The Linux Wacom Project manages the drivers, libraries, and documentation
+ * for configuring and running Wacom tablets under the Linux operating system.
+ * It contains diagnostic applications and X.Org input drivers
+ * for servers 1.7 and later.
+ */
+
#ifndef __XORG_XWACOM_H
#define __XORG_XWACOM_H
@@ -34,15 +43,13 @@
#define ROTATE_CCW 2
#define ROTATE_HALF 3
-#define XWACOM_MAX_SAMPLES 20
-
/* The following flags are used for button action property values to mark
* the type of event that should be emitted when that button is pressed;
* combined together they form an Action Code (AC). Each button has up to
* 256 actions on press, where a zero terminates the actions.
*
* e.g.
- * AC_KEY | AC_KEYBTNPRESS | XK_H is a key press for 'H'.
+ * AC_KEY | AC_KEYBTNPRESS | <keycode> is a key press for key <keycode>.
* AC_BUTTON | AC_KEYBTNPRESS | 1 is a button press for 1
* AC_BUTTON | 1 is a button release for 1
*
@@ -51,12 +58,12 @@
#define AC_CODE 0x0000ffff /* Mask to isolate button number or key code */
#define AC_KEY 0x00010000 /* Emit key events */
#define AC_MODETOGGLE 0x00020000 /* Toggle absolute/relative mode */
-#define AC_DBLCLICK 0x00030000 /* Emit a button1 double-click event */
+#define AC_DBLCLICK 0x00030000 /* DEPRECATED: use two button events instead */
#define AC_DISPLAYTOGGLE 0x00040000 /* Toggle among screens */
#define AC_BUTTON 0x00080000 /* Emit button events */
#define AC_TYPE 0x000f0000 /* The mask to isolate event type bits */
#define AC_KEYBTNPRESS 0x00100000 /* bit set for key/button presses */
-#define AC_CORE 0x10000000 /* Always emit a core event */
+#define AC_CORE 0x10000000 /* DEPRECATED: has no effect */
#define AC_EVENT 0xf00f0000 /* Mask to isolate event flag */
#endif /* __XORG_XWACOM_H */
diff --git a/include/isdv4.h b/include/isdv4.h
new file mode 100644
index 0000000..539f609
--- /dev/null
+++ b/include/isdv4.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2010 by Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef ISDV4_H
+#define ISDV4_H
+
+#define ISDV4_QUERY "*" /* ISDV4 query command */
+#define ISDV4_RESET "&" /* ISDV4 touch panel reset command */
+#define ISDV4_TOUCH_QUERY "%" /* ISDV4 touch query command */
+#define ISDV4_STOP "0" /* ISDV4 stop command */
+#define ISDV4_SAMPLING "1" /* ISDV4 sampling command */
+
+/* packet length for individual models */
+#define ISDV4_PKGLEN_TOUCH93 5
+#define ISDV4_PKGLEN_TOUCH9A 7
+#define ISDV4_PKGLEN_TPCPEN 9
+#define ISDV4_PKGLEN_TPCCTL 11
+#define ISDV4_PKGLEN_TOUCH2FG 13
+
+#define HEADER_BIT 0x80
+#define CONTROL_BIT 0x40
+#define DATA_ID_MASK 0x3F
+#define TOUCH_CONTROL_BIT 0x10
+
+/* Only for touch devices: use serial ID as index to get packet length for device */
+int ISDV4PacketLengths[] = {
+ /* 0x00 => */ ISDV4_PKGLEN_TOUCH93,
+ /* 0x01 => */ ISDV4_PKGLEN_TOUCH9A,
+ /* 0x02 => */ ISDV4_PKGLEN_TOUCH93,
+ /* 0x03 => */ ISDV4_PKGLEN_TOUCH9A,
+ /* 0x04 => */ ISDV4_PKGLEN_TOUCH9A,
+ /* 0x05 => */ ISDV4_PKGLEN_TOUCH2FG
+};
+
+/* ISDV4 protocol parsing structs. */
+
+/* Query reply data */
+typedef struct {
+ unsigned char data_id; /* always 00H */
+ uint16_t x_max;
+ uint16_t y_max;
+ uint16_t pressure_max;
+ uint8_t tilt_x_max;
+ uint8_t tilt_y_max;
+ uint16_t version;
+} ISDV4QueryReply;
+
+/* Touch Query reply data */
+typedef struct {
+ uint8_t data_id; /* always 01H */
+ uint8_t panel_resolution;
+ uint8_t sensor_id;
+ uint16_t x_max;
+ uint16_t y_max;
+ uint8_t capacity_resolution;
+ uint16_t version;
+} ISDV4TouchQueryReply;
+
+/* Touch Data format. Note that capacity and finger2 are only set for some
+ * devices (0 on all others) */
+typedef struct {
+ uint8_t status; /* touch down/up */
+ uint16_t x;
+ uint16_t y;
+ uint16_t capacity;
+ struct {
+ uint8_t status; /* touch down/up */
+ uint16_t x;
+ uint16_t y;
+ } finger2;
+} ISDV4TouchData;
+
+/* Coordinate data format */
+typedef struct {
+ uint8_t proximity; /* in proximity? */
+ uint8_t tip; /* tip/eraser pressed? */
+ uint8_t side; /* side switch pressed? */
+ uint8_t eraser; /* eraser pressed? */
+ uint16_t x;
+ uint16_t y;
+ uint16_t pressure;
+ uint8_t tilt_x;
+ uint8_t tilt_y;
+} ISDV4CoordinateData;
+
+static int isdv4ParseQuery(const unsigned char *buffer, const size_t len,
+ ISDV4QueryReply *reply)
+{
+ int header, control;
+
+ if (!reply || len < ISDV4_PKGLEN_TPCCTL)
+ return 0;
+
+ header = !!(buffer[0] & HEADER_BIT);
+ control = !!(buffer[0] & CONTROL_BIT);
+
+ if (!header || !control)
+ return -1;
+
+ reply->data_id = buffer[0] & DATA_ID_MASK;
+
+ /* FIXME: big endian? */
+ reply->x_max = (buffer[1] << 9) | (buffer[2] << 2) | ((buffer[6] >> 5) & 0x3);
+ reply->y_max = (buffer[3] << 9) | (buffer[4] << 2) | ((buffer[6] >> 3) & 0x3);
+ reply->pressure_max = (buffer[6] & 0x7) << 7 | buffer[5];
+ reply->tilt_y_max = buffer[7];
+ reply->tilt_x_max = buffer[8];
+ reply->version = buffer[9] << 7 | buffer[10];
+
+ return ISDV4_PKGLEN_TPCCTL;
+}
+
+static int isdv4ParseTouchQuery(const unsigned char *buffer, const size_t len,
+ ISDV4TouchQueryReply *reply)
+{
+ int header, control;
+
+ if (!reply || len < ISDV4_PKGLEN_TPCCTL)
+ return 0;
+
+ header = !!(buffer[0] & HEADER_BIT);
+ control = !!(buffer[0] & CONTROL_BIT);
+
+ if (!header || !control)
+ return -1;
+
+ reply->data_id = buffer[0] & DATA_ID_MASK;
+ reply->sensor_id = buffer[2] & 0x7;
+ reply->panel_resolution = buffer[1];
+ /* FIXME: big endian? */
+ reply->x_max = (buffer[3] << 9) | (buffer[4] << 2) | ((buffer[2] >> 5) & 0x3);
+ reply->y_max = (buffer[5] << 9) | (buffer[6] << 2) | ((buffer[2] >> 3) & 0x3);
+ reply->capacity_resolution = buffer[7];
+ reply->version = buffer[9] << 7 | buffer[10];
+
+ return ISDV4_PKGLEN_TPCCTL;
+}
+
+/* pktlen defines what touch type we parse */
+static int isdv4ParseTouchData(const unsigned char *buffer, const size_t buff_len,
+ const size_t pktlen, ISDV4TouchData *touchdata)
+{
+ int header, touch;
+
+ if (!touchdata || buff_len < pktlen)
+ return 0;
+
+ header = !!(buffer[0] & HEADER_BIT);
+ touch = !!(buffer[0] & TOUCH_CONTROL_BIT);
+
+ if (header != 1 || touch != 1)
+ return -1;
+
+ memset(touchdata, 0, sizeof(*touchdata));
+
+ touchdata->status = buffer[0] & 0x1;
+ /* FIXME: big endian */
+ touchdata->x = buffer[1] << 7 | buffer[2];
+ touchdata->y = buffer[3] << 7 | buffer[4];
+ if (pktlen == ISDV4_PKGLEN_TOUCH9A)
+ touchdata->capacity = buffer[5] << 7 | buffer[6];
+
+ if (pktlen == ISDV4_PKGLEN_TOUCH2FG)
+ {
+ touchdata->finger2.x = buffer[7] << 7 | buffer[8];
+ touchdata->finger2.y = buffer[9] << 7 | buffer[10];
+ touchdata->finger2.status = !!(buffer[0] & 0x2);
+ /* FIXME: is there a fg2 capacity? */
+ }
+
+ return pktlen;
+}
+
+static int isdv4ParseCoordinateData(const unsigned char *buffer, const size_t len,
+ ISDV4CoordinateData *coord)
+{
+ int header, control;
+
+ if (!coord || len < ISDV4_PKGLEN_TPCPEN)
+ return 0;
+
+ header = !!(buffer[0] & HEADER_BIT);
+ control = !!(buffer[0] & TOUCH_CONTROL_BIT);
+
+ if (header != 1 || control != 0)
+ return -1;
+
+ coord->proximity = (buffer[0] >> 5) & 0x1;
+ coord->tip = buffer[0] & 0x1;
+ coord->side = (buffer[0] >> 1) & 0x1;
+ coord->eraser = (buffer[0] >> 2) & 0x1;
+ /* FIXME: big endian */
+ coord->x = (buffer[1] << 9) | (buffer[2] << 2) | ((buffer[6] >> 5) & 0x3);
+ coord->y = (buffer[3] << 9) | (buffer[4] << 2) | ((buffer[6] >> 3) & 0x3);
+
+ coord->pressure = ((buffer[6] & 0x7) << 7) | buffer[5];
+ coord->tilt_x = buffer[7];
+ coord->tilt_y = buffer[8];
+
+ return ISDV4_PKGLEN_TPCPEN;
+}
+
+#endif /* ISDV4_H */
+
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/include/wacom-properties.h b/include/wacom-properties.h
index d4b1112..0bb84b1 100644
--- a/include/wacom-properties.h
+++ b/include/wacom-properties.h
@@ -33,28 +33,38 @@
/* 32 bit, 4 values */
#define WACOM_PROP_PRESSURECURVE "Wacom Pressurecurve"
-/* 32 bit, 4 values, tablet id, old serial, old device id, serial */
+/* CARD32, 4 values, tablet id, old serial, old hw device id, current serial
+ read-only */
#define WACOM_PROP_SERIALIDS "Wacom Serial IDs"
-/* 8 bit, 4 values, left up, left down, right up, right down */
+/* CARD32, 1 value */
+#define WACOM_PROP_SERIAL_BIND "Wacom Serial ID binding"
+
+/* 8 bit, 4 values, left up, left down, right up, right down
+ OR
+ Atom, 4 values , left up, left down, right up, right down
+ */
#define WACOM_PROP_STRIPBUTTONS "Wacom Strip Buttons"
-/* 8 bit, 4 values, up, down, wheel up, wheel down */
+/* 8 bit, 6 values, rel wheel up, rel wheel down, abs wheel up, abs wheel down, abs wheel 2 up, abs wheel 2 down
+ OR
+ Atom, 6 values , rel wheel up, rel wheel down, abs wheel up, abs wheel down, abs wheel 2 up, abs wheel 2 down
+ */
#define WACOM_PROP_WHEELBUTTONS "Wacom Wheel Buttons"
-/* 32 bit, 4 values */
+/* DEPRECATED, DO NOT USE */
#define WACOM_PROP_TWINVIEW_RES "Wacom TwinView Resolution"
-/* 8 bit 3 values, screen number, twinview on/off, multimonitor */
+/* DEPRECATED. DO NOT USE */
#define WACOM_PROP_DISPLAY_OPTS "Wacom Display Options"
-/* 32 bit, 4 values, top x, top y, bottom x, bottom y */
+/* DEPRECATED. DO NOT USE */
#define WACOM_PROP_SCREENAREA "Wacom Screen Area"
/* 32 bit, 1 value */
#define WACOM_PROP_PROXIMITY_THRESHOLD "Wacom Proximity Threshold"
-/* 32 bit, 1 value */
+/* DEPRECATED. DO NOT USE */
#define WACOM_PROP_CAPACITY "Wacom Capacity"
/* 32 bit, 1 value */
@@ -66,7 +76,14 @@
/* BOOL, 1 value */
#define WACOM_PROP_TOUCH "Wacom Enable Touch"
-/* BOOL, 1 value */
+/* 8 bit, 1 values */
+#define WACOM_PROP_ENABLE_GESTURE "Wacom Enable Touch Gesture"
+
+/* 32 bit, 3 values, zoom, rotate, tap parameters */
+#define WACOM_PROP_GESTURE_PARAMETERS "Wacom Touch Gesture Parameters"
+
+/* BOOL, 1 value,
+ TRUE == hover click is enabled, FALSE == hover click disabled */
#define WACOM_PROP_HOVER "Wacom Hover Click"
/* Atom, 1 value, read-only */
@@ -84,4 +101,15 @@
*/
#define WACOM_PROP_DEBUGLEVELS "Wacom Debug Levels"
+
+/* The following are tool types used by the driver in WACOM_PROP_TOOL_TYPE
+ * or in the 'type' field for XI1 clients. Clients may check for one of
+ * these types to identify tool types.
+ */
+#define WACOM_PROP_XI_TYPE_STYLUS "STYLUS"
+#define WACOM_PROP_XI_TYPE_CURSOR "CURSOR"
+#define WACOM_PROP_XI_TYPE_ERASER "ERASER"
+#define WACOM_PROP_XI_TYPE_PAD "PAD"
+#define WACOM_PROP_XI_TYPE_TOUCH "TOUCH"
+
#endif
diff --git a/include/wacom-util.h b/include/wacom-util.h
new file mode 100644
index 0000000..ed42947
--- /dev/null
+++ b/include/wacom-util.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 by Jason Gerecke, Wacom. <jason.gerecke@wacom.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef WACOM_UTIL_H_
+#define WACOM_UTIL_H_
+
+/**
+ * Get the number of elements in an array
+ */
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+/* to access kernel defined bits */
+#define BIT(x) (1UL<<((x) & (BITS_PER_LONG - 1)))
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define ISBITSET(x,y) ((x)[LONG(y)] & BIT(y))
+#define SETBIT(x,y) ((x)[LONG(y)] |= BIT(y))
+#define CLEARBIT(x,y) ((x)[LONG(y)] &= ~BIT(y))
+#define OFF(x) ((x)%BITS_PER_LONG)
+#define LONG(x) ((x)/BITS_PER_LONG)
+
+/**
+ * Test if the mask is set in the given bitfield.
+ * @return TRUE if set or FALSE otherwise.
+ */
+#define MaskIsSet(bitfield, mask) !!(((bitfield) & (mask)) == (mask))
+/**
+ * Set the given mask for the given bitfield.
+ */
+#define MaskSet(bitfield, mask) ((bitfield) |= (mask))
+/**
+ * Clear the given mask from the given bitfield
+ */
+#define MaskClear(bitfield, mask) ((bitfield) &= ~(mask))
+
+#endif /* WACOM_UTIL_H_ */
diff --git a/man/Makefile.am b/man/Makefile.am
index 7bfc01b..ec0add2 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -36,29 +36,14 @@ EXTRA_DIST = @DRIVER_NAME@.man xsetwacom.man
CLEANFILES = $(driverman_DATA) $(xsetwacomman_DATA)
-SED = sed
-
-# Strings to replace in man pages
-XORGRELSTRING = @PACKAGE_STRING@
- XORGMANNAME = X Version 11
-
-MAN_SUBSTS = \
- -e 's|__vendorversion__|"$(XORGRELSTRING)" "$(XORGMANNAME)"|' \
- -e 's|__xorgversion__|"$(XORGRELSTRING)" "$(XORGMANNAME)"|' \
- -e 's|__xservername__|Xorg|g' \
- -e 's|__xconfigfile__|xorg.conf|g' \
- -e 's|__projectroot__|$(prefix)|g' \
- -e 's|__appmansuffix__|$(APP_MAN_SUFFIX)|g' \
- -e 's|__drivermansuffix__|$(DRIVER_MAN_SUFFIX)|g' \
- -e 's|__adminmansuffix__|$(ADMIN_MAN_SUFFIX)|g' \
- -e 's|__miscmansuffix__|$(MISC_MAN_SUFFIX)|g' \
- -e 's|__filemansuffix__|$(FILE_MAN_SUFFIX)|g' \
- -e 's|__drivername__|$(DRIVER_NAME)|g'
-
SUFFIXES = .$(DRIVER_MAN_SUFFIX) $(APP_MAN_SUFFIX) .man
-.man.$(DRIVER_MAN_SUFFIX):
- sed $(MAN_SUBSTS) < $< > $@
+# String replacements in MAN_SUBSTS now come from xorg-macros.m4 via configure
+MAN_SUBSTS += \
+ -e 's|__drivername__|$(DRIVER_NAME)|g'
+.man.$(DRIVER_MAN_SUFFIX):
+ $(AM_V_GEN)$(SED) $(MAN_SUBSTS) < $< > $@
.man.$(APP_MAN_SUFFIX):
- sed $(MAN_SUBSTS) < $< > $@
+ $(AM_V_GEN)$(SED) $(MAN_SUBSTS) < $< > $@
+
diff --git a/man/wacom.man b/man/wacom.man
index 1bccb93..3d4a143 100644
--- a/man/wacom.man
+++ b/man/wacom.man
@@ -1,7 +1,7 @@
.ds q \N'34'
.TH WACOM __drivermansuffix__ __vendorversion__
.SH NAME
-wacom \- Wacom input driver
+__drivername__ \- Wacom input driver
.SH SYNOPSIS
.nf
.B "Section \*qInputDevice\*q"
@@ -17,20 +17,53 @@ is an X input driver for Wacom devices.
.PP
The
.B wacom
-driver functions as a pointer input device, and may be used as the
-X server's core pointer.
+driver functions as a pointer input device.
.SH SUPPORTED HARDWARE
This driver supports the Wacom IV and Wacom V protocols. Serial tablets only
-need this driver. USB tablet support is available on some Linux platforms.
-USB tablets needs wacom kernel driver being loaded before this driver starts.
-Please check linuxwacom.sf.net for latest updates of Wacom X and kernel drivers.
+need this driver. USB tablet support is available on some Linux platforms
+and requires the wacom kernel driver being loaded before this driver starts.
+.PP
+Please check http://linuxwacom.sourceforge.net for latest updates of Wacom X
+and kernel drivers.
+.SH DRIVER-INTERNAL DEVICE HOTPLUGGING
+When input device hotplugging in the X server is enabled and no
+.B InputDevice
+section exists for a compatible tablet device and an
+.B InputClass
+section (see xorg.conf.d(5x)) assigns this driver for the device, the
+.B wacom
+driver creates multiple X devices for each a physical device, one X device
+for each available tool. The list of tools is hardware-dependent. See
+.B Option "Type"
+as outlined in the
+.B CONFIGURATION DETAILS
+section.
+.PP
+These tool-specific devices are referred to as parent device and dependent
+device. The parent device is the one presented by the system and the one
+that causes the X server to load the
+.B wacom
+driver. This parent device then causes the automatic addition of several
+dependent devices. If the parent device is unplugged or otherwise removed,
+the dependent devices will be automatically removed as well.
+.PP
+Dependent devices may be assigned tool-specific options through additional
+.B InputClass
+sections. We recommend that a
+.B MatchDriver "wacom"
+line is used in these sections in addition to the user-specific pattern.
+.PP
+Match directives are applied by the X server before the driver is selected.
+The type name of the parent device is appended by the driver. It is not
+possible to use a
+.B MatchProduct
+directive to match against this appended type name.
.SH CONFIGURATION DETAILS
-Please refer to xorg.conf(5x) for general configuration
+Please refer to xorg.conf(5x) or xorg.conf.d(5x) for general configuration
details and for options that can be used with all input drivers. This
section only covers configuration details specific to this driver.
.PP
-Multiple instances of the Wacom devices can cohabit. It can be useful
-to define multiple devices with different active zones. Each device
+Multiple instances of the Wacom devices can cohabit. Each device
supports the following entries:
.RS 8
.TP 4
@@ -51,11 +84,6 @@ the tablet is plugged. You have to specify it for each subsection with
the same value if you want to have multiple devices with the same tablet.
This option is mandatory.
.TP 4
-.B Option \fI"ForceDevice"\fP \fI"ISDV4"\fP
-tells the driver to dialog with the tablet the serial Tablet PC way. It
-is a special Wacom IV protocol, called ISDV4 protocol. This option is
-mandatory for serial Tablet PCs only.
-.TP 4
.B Option \fI"Suppress"\fP \fI"number"\fP
sets the position increment under which not to transmit coordinates.
This entry must be specified only in the first Wacom subsection if you have
@@ -123,87 +151,60 @@ To ignore the button click, i.e., to not report any button click event
to the X server, use "0" or "button 0".
.TP 4
.B Option \fI"TPCButton"\fP \fI"on"|"off"\fP
-enables the stylus buttons as Tablet PC buttons, i.e., reports stylus
-button event only when its tip is pressed. Default to "on" for Tablet PCs;
-"off" for all other models.
+enables the stylus buttons as Tablet PC buttons. If enabled, the stylus
+reports button events only when its tip is pressed. If any button is down,
+pressing the tip reports a button event for the button. Otherwise if no
+button is down, the tip reports as usual.
+Default to "on" for Tablet PCs; "off" for all other models. Only available
+on the stylus tool.
.TP 4
.B Option \fI"Touch"\fP \fI"on"|"off"\fP
enables the touch event for Tablet PC that supports touch feature, i.e.,
system cursor moves when user touches the tablet. Default to "on" for
Tablet PCs with touch feature; "off" for all other models.
.TP 4
-.B Option \fI"Capacity"\fP \fI"number"\fP
-sets touch sensitivity level for capacitive touch device, where number
-can be an integer from -1 to 5. Default is 3 for capacitive tools and
--1 for none capacitive tools.
-.TP 4
-.B Option \fI"Speed"\fP \fI"Rspeed"\fP
-sets the cursor's relative movement speed to Rspeed. The default value is 1.0.
-A Rspeed greater than 1.0 will speed up the cursor's relative movement. A Rspeed
-less than 1.0 but greater than 0 will slow down the cursor's relative movement.
-A Rspeed too close to 0 is not recommanded.
-.TP 4
-.B Option \fI"Twinview"\fP \fI"horizontal"|"vertical"|"leftof"|"aboveof"|"xinerama"|"none"\fP
-sets the orientation of TwinView to map the tablet to one screen and to be able
-to move the screen cursor from one screen to the other when tool reaches the
-edge of the tablet. The cursor can be constrained in a specific screen if
-"ScreenNo" option is added. If you want to map the tablet to the whole desktop,
-you should NOT add this option. The default is "none". Note: due to historic
-reason, "horizontal" represents the "RightOf" and "vertical" represents the
-"BelowOf" in acutal TwinView setup.
-.TP 4
-.B Option \fI"TVResolution"\fP \fI"res0,res1"\fP
-specifies different resolutions for the two screens in TwinView setup. For example,
-if the resolution of screen 0 (res0) is 1024x768 and screen 1 (res1) is 1280x1024,
-the option will be set to:
- Option "TVResolution" "1024x768,1280x1024"
-
-This option is used only when TwinView option is not none. It is unnecessary to add
-this option if your screens are displaying in the same resolutions.
-.TP 4
-.B Option \fI"ScreenNo"\fP \fI"n"\fP
-In a multi-monitor environment, specifies the screen number in which the cursor can move.
-.TP 4
-.B Option \fI"MMonitor"\fP \fI"n"\fP
-turns on/off across monitor movement on a non-TwinView multi-monitor desktop.
-The default is "on". Here n starts from 0, which indicates the first screen.
-.TP 4
.B Option \fI"Rotate"\fP \fI"CW"|"CCW"|"HALF"|"NONE"\fP
rotates the tablet orientation counterclockwise (CCW) or clockwise (CW) or 180 degrees (HALF).
If you have specific tablet mappings, i.e. TopX/Y or BottomX/Y were set, the mapping will be
-applied before rotation. The default is "NONE".
+applied before rotation. Rotation must be applied to the parent device
+(usually the stylus), rotation settings on in-driver hotplugged devices (see
+.B DRIVER-INTERNAL DEVICE HOTPLUGGING
+) will be ignored. The default is "NONE".
.TP 4
.B Option \fI"PressCurve"\fP \fI"x1,y1,x2,y2"\fP
sets pressure curve by control points x1, y1, x2, and y2. Their values are in range
-from 0..100. The input for
- linear curve (default) is "0,0,100,100";
- slightly depressed curve (firmer) might be "5,0,100,95";
- slightly raised curve (softer) might be "0,5,95,100".
+from 0..100. The pressure curve is interpreted as Bezier curve with 4
+control points, the first and the last control point being fixed on the
+coordinates 0/0 and 100/100, respectively. The middle control points are
+adjustible by this setting and thus define the shape of the curve.
+The input for linear curve (default) is "0,0,100,100";
+slightly depressed curve (firmer) might be "5,0,100,95";
+slightly raised curve (softer) might be "0,5,95,100".
The pressure curve is only applicable to devices of type stylus or eraser,
other devices do not honor this setting.
.TP 4
-.B Option \fI"KeepShape"\fP \fI"on"|"off"\fP
-When this option is enabled, the active zone begins according to TopX
-and TopY. The bottom corner is adjusted to keep the ratio width/height
-of the active zone the same as the screen while maximizing the area
-described by TopX, TopY, BottomX, BottomY.
-.TP 4
.B Option \fI"DebugLevel"\fP \fI"number"\fP
-sets the level of debugging info reported. There are 12 levels, specified by
-the integers between 1 and 12. Once it is defined, all the debug messages
-with a level less than or equal to the "number" will be logged into
-/etc/X11/Xorg.0.log.
+sets the level of debugging info for tool-specific messages. There are 12
+levels, specified by the integers between 1 and 12. All debug messages with
+a level less than or equal to the "number" will be logged into the Xorg log
+file. This option is only available if the driver was built with debugging
+support.
.TP 4
.B Option \fI"CommonDBG"\fP \fI"number"\fP
-sets the level of debugging info for all devices defined for the
-same tablet. There are 12 levels in use, specified by the
-integers between 1 and 12. Once it is defined, all the debug
-messages with a level less than or equal to the "number" will
-be logged into /etc/X11/Xorg.0.log.
+sets the level of debugging info for common (i.e. not tool-specific) code
+paths on the tablet. There are 12 levels, specified by the integers between
+1 and 12. All debug messages with a level less than or equal to the
+"number" will be logged into the Xorg log file. This option is only
+available if the driver was built with debugging support.
.TP 4
.B Option \fI"CursorProx"\fP \fI"number"\fP
sets the max distance from tablet to stop reporting movement for cursor in relative mode.
-Default for Intuos series is 10, for Graphire series (including Volitos) is 42.
+Default for Intuos series is 10, for Graphire series (including Volitos) is
+42. Only available for the cursor/puck device.
+.TP 4
+.B Option \fI"RawSample"\fP \fI"number"\fP
+Set the sample window size (a sliding average sampling window) for
+incoming input tool raw data points. Default: 4, range of 1 to 20.
.TP 4
.B Option \fI"Serial"\fP \fI"number"\fP
sets the serial number associated with the physical device. This allows
@@ -212,14 +213,52 @@ option is only available on wacom V devices (Intuos series and Cintiq 21U).
To see which serial number belongs to a device, you need to run the utility program,
xsetwacom, which comes with linuxwacom package.
.TP 4
+.B Option \fI"ToolSerials"\fP \fI"number[,type[,label]][;...]"\fP
+sets the list of serial numbered devices that need to be hotplugged for a physical
+device. The 'type' option may be any of "pen", "airbrush", "artpen", or "cursor".
+This option is only available on wacom V devices (Intuos series and Cintiq 21U).
+To see which serial number belongs to a device, you need to run the utility program,
+xsetwacom, that comes with this driver.
+.TP 4
.B Option \fI"Threshold"\fP \fI"number"\fP
sets the pressure threshold used to generate a button 1 events of stylus.
-The default is MaxPressure*3/50.
+The threshold applies to the normalised pressure range of [0..2048].
+The default is 27.
+.TP 4
+.B Option \fI"Gesture"\fP \fI"bool"\fP
+Enable or disable gesture support on the device. Default: off unless the
+tablet supports multi-touch.
+.TP 4
+.B Option \fI"ZoomDistance"\fP \fI"number"\fP
+If
+.B Option \fI"Gesture"\fP
+is enabled, this option specifies the minimum movement distance before a
+zoom gesture is recognized.
+.TP 4
+.B Option \fI"ScrollDistance"\fP \fI"number"\fP
+If
+.B Option \fI"Gesture"\fP
+is enabled, this option specifies the minimum movement distance before a
+scroll gesture is recognized.
+.TP 4
+.B Option \fI"TapTime"\fP \fI"number"\fP
+If
+.B Option \fI"Gesture"\fP
+is enabled, this option determines the maximum time of a touch event to be
+recognized as tap. A press and release event shorter than
+.B TapTime
+generates button press and release events. Presses longer than
+.B TapTime
+do not generate button events, only motion events.
.RE
.SH "SEE ALSO"
-Xorg(1x), xorg.conf(5x), xorgconfig(1x), Xserver(1x), X(7).
+__xservername__(__appmansuffix__), xorg.conf(__filemansuffix__),
+xorg.conf.d(__filemansuffix__), X(__miscmansuffix__)
+.PP
+More information is available at http://linuxwacom.sourceforge.net
.SH AUTHORS
-Frederic Lepied <lepied@xfree86.org>,
+Peter Hutterer <peter.hutterer@who-t.net>,
Ping Cheng <pingc@wacom.com>,
+Frederic Lepied <lepied@xfree86.org>,
John E. Joganic <jej@j-arkadia.com>,
Magnus Vigerlöf <Magnus.Vigerlof@ipbo.se>
diff --git a/man/xsetwacom.man b/man/xsetwacom.man
index f220d91..dc0995f 100644
--- a/man/xsetwacom.man
+++ b/man/xsetwacom.man
@@ -6,7 +6,7 @@
xsetwacom \- commandline utility to query and modify __drivername__ driver settings.
.SH "SYNOPSIS"
.LP
-xsetwacom [options] command [device_name] [parameter] [value]
+xsetwacom [options] [command [arguments]]
.SH "DESCRIPTION"
.LP
@@ -44,19 +44,18 @@ and
.SS "LIST COMMANDS"
.TP
-\fBlist\fR dev
+\fBlist\fR devices
List known devices. Only input devices managed by the __drivername__
driver are listed.
.TP
-\fBlist\fR param
+\fBlist\fR parameters
List known parameters. List all parameters suitable for the
.B get
or the
.B set
command. Note that not all parameters are available on all device types.
.TP
-\fBlist\fR mod
-.B Not implemented!
+\fBlist\fR modifiers
List the available list of modifiers to be used when setting key or button
actions.
@@ -67,7 +66,8 @@ Get the current settings for the parameter on the given device. Note that
not all parameters are available on all device types. The special parameter
name "all" may be provided to display all current settings on the device.
.TP
-By default, options are printed on the commandline in the respective format. The output format may be altered with one of the following options:
+By default, options are printed on the commandline in the respective format.
+The output format may be altered with one of the following options:
.TP
\fB-s, --shell\fR
Display the output in shell format, i.e. as shell commands to xsetwacom to
@@ -79,15 +79,149 @@ added to the InputDevice section in the xorg.conf.
.SS "SET COMMANDS"
.TP
-\fBset\fR device_name parameter value
+\fBset\fR device_name parameter [value]
Set the parameter value on the given device to the value provided. Note that
not all parameters are writable, some are read-only and result in an error
when trying to be modified.
+.SH "PARAMETERS"
+.LP
+Not all parameters are available on all tools. Use the get command with the
+parameter or "all" parameter for specific input tool applicable parameters
+and current settings.
+.TP
+\fBArea\fR x1 y1 x2 y2
+Set the tablet input area in device coordinates in the form top left
+x/y and bottom right x/y. Top left and bottom right are defined in the
+device's native orientation, regardless of the actual rotation currently
+applied. Input outside of these coordinates will be clipped to the edges
+of the area defined. Default: 0 0 x2 y2; with x2 and y2 tablet specific.
+.TP
+\fBButton\fR button-number [mapping]
+Set a mapping for the specified button-number. Mappings take the form of
+either a single numeric button or an 'action' to be performed. If no mapping
+is provided, the default mapping is restored.
+
+Numeric button mappings indicate what X11 button number the given button-number
+should correspond to. For example, a mapping of "3" means a press of the given
+button-number will produce as a press of X11 button 3 (i.e. right click).
+
+Action mappings allow button presses to perform many events. They take the form
+of a string of keywords and arguments. For example, "key +a +shift b -shift -a"
+converts the button into a series of keystrokes, in this example "press a, press
+shift, press and release b, release shift, release a". In addition to the "key"
+keyword, "button" and "modetoggle" are also recognized. Multiple keywords may
+be present in one action if desired: for example "key +ctrl button 5 key -ctrl".
+.TP
+\fBBindToSerial\fR [serial|0]
+Bind the device to the tool with the specified serial number. Once bound,
+the device will ignore events from other tools. A serial of 0 means the
+device is unbound and will react to any tool of the matching type.
+Default: 0
+.TP
+\fBMapToOutput\fR [output]
+Map the tablet's input area to a given output (e.g. "VGA1"). Output names may
+either be the name of a head available through the XRandR extension, or an
+X11 geometry string of the form WIDTHxHEIGHT+X+Y. To switch to the next
+available output, the "next" keyword is also supported. This will cycle
+between the individual monitors connected to the system, and then the entire
+desktop. The mapping may be reset to the entire desktop at any time with the
+output name "desktop". Users of the NVIDIA binary driver should use the output
+names "HEAD-0" and "HEAD-1" until the driver supports XRandR 1.2 or later.
+
+The output mapping configuration is a onetime setting and does not track output
+reconfigurations; the command needs to be re-run whenever the output
+configuration changes. When used with tablet rotation, the tablet must be
+rotated before it is mapped to the new screen. This parameter is write-only
+and cannot be queried.
+.TP
+\fBMode\fR Absolute|Relative
+Set the device mode as either Relative or Absolute. Relative means pointer
+tracking for the device will function like a mouse, whereas Absolute means
+the pointer corresponds to the device's actual position on the tablet or
+tablet PC screen. Default: Absolute for stylus, eraser and tablet PC touch;
+Relative for cursor and tablet touch.
+.TP
+\fBPressureCurve\fR x1 y1 x2 y2
+A Bezier curve of third order, composed of two anchor points (0,0 and 100,100)
+and two user modifiable control points that define the curve's shape. Raise
+the curve (x1<y1 x2<y2) to "soften" the feel and lower the curve (x1>y1 x2>y2)
+for a "firmer" feel. Sigmoid shaped curves are permitted (x1>y1 x2<y2 or
+x1<y1 x2>y2). Default: 0 0 100 100, a linear curve; range of 0 to 100 for
+all four values.
+.TP
+\fBRawSample\fR level
+Set the sample window size (a sliding average sampling window) for incoming
+input tool raw data points. Default: 4, range of 1 to 20.
+.TP
+\fBRotate\fR none|half|cw|ccw
+Set the tablet to the given rotation:
+ none: the tablet is not rotated and uses its natural rotation
+ half: the tablet is rotated by 180 degrees (upside-down)
+ cw: the tablet is rotated 90 degrees clockwise
+ ccw: the tablet is rotated 90 degrees counter-clockwise
+
+Rotation is a tablet-wide option: rotation of one tool affects all other tools
+associated with the same tablet. When the tablet is physically rotated, rotate
+any tool to the corresponding orientation. Default: none
+.TP
+\fBSuppress\fR level
+Set the delta (difference) cutoff level for further processing of incoming
+input tool coordinate values. For example a X or Y coordinate event will be
+sent only if the change between the current X or Y coordinate and the
+previous one is greater than the Suppress value. The same applies to
+pressure level (Z coordinate) and Tilt rotation values. With a current
+absolute wheel (AbsWheel) or Tilt value the delta between it and the
+previous value must be equal to or greater than the Suppress value in order
+to be sent on. Suppress is a tablet wide parameter. A specified delta
+level for one input tool is applied to all input tool coordinates. To
+disable suppression use a level of 0. Default: 2, range of 0 to 100.
+.TP
+\fBTabletDebugLevel\fR level
+Set the debug level for this tablet to the given level. This only affects
+code paths that are shared between several tools on the same physical
+tablet. A higher level means more fine-grained debug messages, a level of 0
+turns debugging off for this tool. Requires the driver to be built with
+debugging enabled. See also ToolDebugLevel. Default: 0, range of 0 to 12.
+.TP
+\fBTabletPCButton\fR on|off
+If on, the stylus must be in contact with the screen for a stylus side button
+to work. If off, stylus buttons will work once the stylus is in proximity
+of the tablet (regardless of whether it is touching the screen). Default: on
+for Tablet PCs; off for all other models.
+.TP
+\fBToolSerialPrevious\fR
+Get the serial number of the tool that was last in proximity last. This
+serial number is updated whenever the tool goes out of proximity. If the
+current tool went out of proximity once, this serial number is the one of
+the current tool. This is a read-only parameter.
+.TP
+\fBCursorProximity\fR distance
+sets the max distance from tablet to stop reporting movement for cursor in
+relative mode. Default for Intuos series is 10, for Graphire series (including
+Volitos) is 42. Only available for the cursor/puck device.
+.TP
+\fBThreshold\fR level
+Set the minimum pressure necessary to generate a Button event for the stylus
+tip, eraser, or touch. The pressure levels of all tablets are normalized to
+2048 levels irregardless of the actual hardware supported levels. This
+parameter is independent of the PressureCurve parameter. Default: 27,
+range of 0 to 2047.
+.TP
+\fBToolDebugLevel\fR level
+Set the debug level for this tool to the given level. This only affects
+code paths that are specific to a given tool. A higher level means more
+fine-grained debug messages, a level of 0 turns debugging off for this
+tool. Requires the driver to be built with debugging enabled. See also
+TabletDebugLevel. Default: 0, range of 0 to 12.
+
+
.SH "AUTHORS"
Peter Hutterer <peter.hutterer@redhat.com>
.SH "SEE ALSO"
-__xservername__(__appmansuffix__), wacom(__drivermansuffix__),
+__xservername__(__appmansuffix__), __drivername__(__drivermansuffix__),
xorg.conf(__filemansuffix__),
X(__miscmansuffix__)
+.PP
+More information is available at http://linuxwacom.sourceforge.net
diff --git a/release.sh b/release.sh
new file mode 100755
index 0000000..3fe3a56
--- /dev/null
+++ b/release.sh
@@ -0,0 +1,227 @@
+#!/bin/sh
+# Release script for xf86-input-wacom.
+# This is essentially a copy of the X.Org util/modular/release.sh script
+# with a few modified parameters.
+
+set -e
+
+announce_list="linuxwacom-announce@lists.sourceforge.net"
+discuss_list="linuxwacom-discuss@lists.sourceforge.net"
+module=xf86-input-wacom
+user=${USER}@
+host=shell.sourceforge.net
+srv_path=/home/frs/project/l/li/linuxwacom/$module
+webpath=sourceforge.net/projects/linuxwacom/files/$module
+remote=origin
+
+usage()
+{
+ cat <<HELP
+Usage: `basename $0` [options] <tag_previous> <tag_current>
+
+Options:
+ --force force overwritting an existing release
+ --user <name> username on $host
+ --help this help message
+ --ignore-local-changes don't abort on uncommitted local changes
+ --remote git remote where the change should be pushed (default "origin")
+HELP
+}
+
+abort_for_changes()
+{
+ cat <<ERR
+Uncommitted changes found. Did you forget to commit? Aborting.
+Use --ignore-local-changes to skip this check.
+ERR
+ exit 1
+}
+
+gen_announce_mail()
+{
+case "$tag_previous" in
+initial)
+ range="$tag_current"
+ ;;
+*)
+ range="$tag_previous".."$tag_current"
+ ;;
+esac
+
+MD5SUM=`which md5sum || which gmd5sum`
+SHA1SUM=`which sha1sum || which gsha1sum`
+
+ cat <<RELEASE
+Subject: [ANNOUNCE] $module $version
+To: $announce_list
+Reply-To: $discuss_list
+
+`git log --no-merges "$range" | git shortlog`
+
+git tag: $tag_current
+
+http://$webpath/$tarbz2/download
+MD5: `cd $tarball_dir && $MD5SUM $tarbz2`
+SHA1: `cd $tarball_dir && $SHA1SUM $tarbz2`
+
+http://$webpath/$targz/download
+MD5: `cd $tarball_dir && $MD5SUM $targz`
+SHA1: `cd $tarball_dir && $SHA1SUM $targz`
+
+RELEASE
+}
+
+export LC_ALL=C
+
+while [ $# != 0 ]; do
+ case "$1" in
+ --force)
+ force="yes"
+ shift
+ ;;
+ --help)
+ usage
+ exit 0
+ ;;
+ --user)
+ shift
+ user=$1@
+ shift
+ ;;
+ --ignore-local-changes)
+ ignorechanges=1
+ shift
+ ;;
+ --remote)
+ shift
+ remote=$1
+ shift
+ ;;
+ --*)
+ echo "error: unknown option"
+ usage
+ exit 1
+ ;;
+ *)
+ tag_previous="$1"
+ tag_current="$2"
+ shift 2
+ if [ $# != 0 ]; then
+ echo "error: unknown parameter"
+ usage
+ exit 1
+ fi
+ ;;
+ esac
+done
+
+# Check for uncommitted/queued changes.
+if [ "x$ignorechanges" != "x1" ]; then
+ set +e
+ git diff --exit-code > /dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ abort_for_changes
+ fi
+ set -e
+fi
+
+# Check if the object has been pushed. Do do so
+# 1. Check if the current branch has the object. If not, abort.
+# 2. Check if the object is on $remote/branchname. If not, abort.
+local_sha=`git rev-list -1 $tag_current`
+current_branch=`git branch | grep "\*" | sed -e "s/\* //"`
+set +e
+git rev-list $current_branch | grep $local_sha > /dev/null
+if [ $? -eq 1 ]; then
+ echo "Cannot find tag '$tag_current' on current branch. Aborting."
+ echo "Switch to the correct branch and re-run the script."
+ exit 1
+fi
+
+revs=`git rev-list $remote/$current_branch..$current_branch | wc -l`
+if [ $revs -ne 0 ]; then
+ git rev-list $remote/$current_branch..$current_branch | grep $local_sha > /dev/null
+
+ if [ $? -ne 1 ]; then
+ echo "$remote/$current_branch doesn't have object $local_sha"
+ echo "for tag '$tag_current'. Did you push branch first? Aborting."
+ exit 1
+ fi
+fi
+set -e
+
+tarball_dir="$(dirname $(find . -name config.status))"
+module="${tag_current%-*}"
+if [ "x$module" = "x$tag_current" ]; then
+ # version-number-only tag.
+ pwd=`pwd`
+ module=`basename $pwd`
+ version="$tag_current"
+else
+ # module-and-version style tag
+ version="${tag_current##*-}"
+fi
+
+detected_module=`grep 'PACKAGE = ' $tarball_dir/Makefile | sed 's|PACKAGE = ||'`
+if [ -f $detected_module-$version.tar.bz2 ]; then
+ module=$detected_module
+fi
+
+modulever=$module-$version
+tarbz2="$modulever.tar.bz2"
+targz="$modulever.tar.gz"
+announce="$tarball_dir/$modulever.announce"
+
+echo "checking parameters"
+if ! [ -f "$tarball_dir/$tarbz2" ] ||
+ ! [ -f "$tarball_dir/$targz" ] ||
+ [ -z "$tag_previous" ]; then
+ echo "error: incorrect parameters!"
+ usage
+ exit 1
+fi
+
+echo "checking for proper current dir"
+if ! [ -d .git ]; then
+ echo "error: do this from your git dir, weenie"
+ exit 1
+fi
+
+echo "checking for an existing tag"
+if ! git tag -l $tag_current >/dev/null; then
+ echo "error: you must tag your release first!"
+ exit 1
+fi
+
+echo "creating shell on sourceforge for $USER"
+echo "Simply log out once you get to the prompt"
+ssh -t ${user/%@},linuxwacom@shell.sourceforge.net create
+
+echo "Sleeping for 30 seconds, because this sometimes helps against sourceforge's random authentication denials"
+sleep 30
+
+echo "checking for an existing release"
+if ssh $user$host ls $srv_path/$module/$targz >/dev/null 2>&1 ||
+ssh $user$host_people ls $srv_path/$module/$tarbz2 >/dev/null 2>&1; then
+if [ "x$force" = "xyes" ]; then
+echo "warning: overriding released file ... here be dragons."
+else
+echo "error: file already exists!"
+exit 1
+fi
+fi
+
+echo "generating announce mail template, remember to sign it"
+gen_announce_mail >$announce
+echo " at: $announce"
+
+echo "Sleeping for 30 seconds, because this sometimes helps against sourceforge's random authentication denials"
+sleep 30
+
+echo "installing release into server"
+scp $tarball_dir/$targz $tarball_dir/$tarbz2 $user$host:$srv_path
+
+echo "pushing tag upstream"
+git push $remote $tag_current
+
+echo "All done. Please bump configure.ac to x.y.99 now"
diff --git a/src/Makefile.am b/src/Makefile.am
index e332ca8..b9fecc6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -25,17 +25,13 @@
# _ladir passes a dummy rpath to libtool so the thing will actually link
# TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc.
-AM_CFLAGS = $(XORG_CFLAGS) $(CWARNFLAGS)
+include common.mk
@DRIVER_NAME@_drv_la_LTLIBRARIES = @DRIVER_NAME@_drv.la
@DRIVER_NAME@_drv_la_LDFLAGS = -module -avoid-version
@DRIVER_NAME@_drv_ladir = @inputdir@
-INCLUDES=-I$(top_srcdir)/include/
-
-@DRIVER_NAME@_drv_la_SOURCES = xf86Wacom.c xf86Wacom.h \
- wcmCommon.c wcmConfig.c wcmISDV4.c wcmISDV4.h \
- wcmFilter.c wcmFilter.h xf86WacomDefs.h \
- wcmUSB.c wcmXCommand.c wcmValidateDevice.c \
- wcmTouchFilter.c
+AM_CPPFLAGS=-I$(top_srcdir)/include/
+AM_CFLAGS = $(XORG_CFLAGS) $(CWARNFLAGS) $(UDEV_CFLAGS)
+@DRIVER_NAME@_drv_la_SOURCES = $(DRIVER_SOURCES)
diff --git a/src/common.mk b/src/common.mk
new file mode 100644
index 0000000..7f1eadd
--- /dev/null
+++ b/src/common.mk
@@ -0,0 +1,16 @@
+# shared makefile between src/Makefile.am and test/Makefile.am
+
+DRIVER_SOURCES= \
+ $(top_srcdir)/src/xf86Wacom.c \
+ $(top_srcdir)/src/xf86Wacom.h \
+ $(top_srcdir)/src/wcmCommon.c \
+ $(top_srcdir)/src/wcmConfig.c \
+ $(top_srcdir)/src/wcmISDV4.c \
+ $(top_srcdir)/src/wcmFilter.c \
+ $(top_srcdir)/src/wcmFilter.h \
+ $(top_srcdir)/src/xf86WacomDefs.h \
+ $(top_srcdir)/src/wcmUSB.c \
+ $(top_srcdir)/src/wcmXCommand.c \
+ $(top_srcdir)/src/wcmValidateDevice.c \
+ $(top_srcdir)/src/wcmTouchFilter.c \
+ $(top_srcdir)/src/wcmTouchFilter.h
diff --git a/src/wcmCommon.c b/src/wcmCommon.c
index 15e439f..0f041e3 100644
--- a/src/wcmCommon.c
+++ b/src/wcmCommon.c
@@ -23,186 +23,104 @@
#include "xf86Wacom.h"
#include "Xwacom.h"
+#include "wcmFilter.h"
+#include "wcmTouchFilter.h"
#include <xkbsrv.h>
#include <xf86_OSproc.h>
+/* Tested result for setting the pressure threshold to a reasonable value */
+#define THRESHOLD_TOLERANCE (FILTER_PRESSURE_RES / 125)
+#define DEFAULT_THRESHOLD (FILTER_PRESSURE_RES / 75)
+
+/* X servers pre 1.9 didn't copy data passed into xf86Post*Event.
+ * Data passed in would be modified, requiring the driver to copy the
+ * data beforehand.
+ */
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 11
+static int v[MAX_VALUATORS];
+static int *VCOPY(const int *valuators, int nvals)
+{
+ memcpy(v, valuators, nvals * sizeof(int));
+ return v;
+}
+#else /* ABI >= 11 */
+#define VCOPY(vals, nval) (vals)
+#endif
+
+
+
/*****************************************************************************
* Static functions
****************************************************************************/
-
-static void transPressureCurve(WacomDevicePtr pDev, WacomDeviceStatePtr pState);
-static void commonDispatchDevice(WacomCommonPtr common, unsigned int channel,
- const WacomChannelPtr pChannel, int suppress);
-static void resetSampleCounter(const WacomChannelPtr pChannel);
+
+static int applyPressureCurve(WacomDevicePtr pDev, const WacomDeviceStatePtr pState);
+static void commonDispatchDevice(WacomCommonPtr common,
+ unsigned int channel,
+ const WacomChannelPtr pChannel,
+ enum WacomSuppressMode suppress);
static void sendAButton(InputInfoPtr pInfo, int button, int mask,
- int rx, int ry, int rz, int v3, int v4, int v5);
+ int first_val, int num_vals, int *valuators);
/*****************************************************************************
- * wcmMappingFactor --
- * calculate the proper tablet to screen mapping factor according to the
- * screen/desktop size and the tablet size
+ * Utility functions
****************************************************************************/
-void wcmMappingFactor(InputInfoPtr pInfo)
+/**
+ * @return TRUE if the device is set to abolute mode, or FALSE otherwise
+ */
+Bool is_absolute(InputInfoPtr pInfo)
{
- WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ return !!(priv->flags & ABSOLUTE_FLAG);
+}
- DBG(10, priv, "\n"); /* just prints function name */
+/**
+ * Set the device to absolute or relative mode
+ *
+ * @param absolute TRUE to set the device to absolute mode.
+ */
+void set_absolute(InputInfoPtr pInfo, Bool absolute)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
- wcmVirtualTabletSize(pInfo);
-
- if (!(priv->flags & ABSOLUTE_FLAG) || !priv->wcmMMonitor)
- {
- /* Get the current screen that the cursor is in */
- if (miPointerGetScreen(pInfo->dev))
- priv->currentScreen = miPointerGetScreen(pInfo->dev)->myNum;
- }
+ if (absolute)
+ priv->flags |= ABSOLUTE_FLAG;
else
- {
- if (priv->screen_no != -1)
- priv->currentScreen = priv->screen_no;
- else if (priv->currentScreen == -1)
- {
- /* Get the current screen that the cursor is in */
- if (miPointerGetScreen(pInfo->dev))
- priv->currentScreen = miPointerGetScreen(pInfo->dev)->myNum;
- }
- }
- if (priv->currentScreen == -1) /* tool on the tablet */
- priv->currentScreen = 0;
-
- DBG(10, priv,
- "Active tablet area x=%d y=%d (virtual tablet area x=%d y=%d) map"
- " to maxWidth =%d maxHeight =%d\n",
- priv->bottomX, priv->bottomY, priv->sizeX, priv->sizeY,
- priv->maxWidth, priv->maxHeight);
-
- priv->factorX = (double)priv->maxWidth / (double)priv->sizeX;
- priv->factorY = (double)priv->maxHeight / (double)priv->sizeY;
- DBG(2, priv, "X factor = %.3g, Y factor = %.3g\n",
- priv->factorX, priv->factorY);
+ priv->flags &= ~ABSOLUTE_FLAG;
}
/*****************************************************************************
- * wcmSetScreen --
- * set to the proper screen according to the converted (x,y).
- * this only supports for horizontal setup now.
- * need to know screen's origin (x,y) to support
- * combined horizontal and vertical setups
+ * wcmMappingFactor --
+ * calculate the proper tablet to screen mapping factor according to the
+ * screen/desktop size and the tablet size
****************************************************************************/
-static void wcmSetScreen(InputInfoPtr pInfo, int v0, int v1)
+void wcmMappingFactor(InputInfoPtr pInfo)
{
WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
- int screenToSet = -1, i, j, x, y, tabletSize = 0;
-
- DBG(6, priv, "v0=%d v1=%d "
- "currentScreen=%d\n", v0, v1, priv->currentScreen);
-
- if (priv->screen_no != -1 && priv->screen_no >= priv->numScreen)
- {
- xf86Msg(X_ERROR, "%s: wcmSetScreen Screen%d is larger than number of available screens (%d)\n",
- pInfo->name, priv->screen_no, priv->numScreen);
- priv->screen_no = -1;
- }
-
- if (!(pInfo->flags & (XI86_ALWAYS_CORE ))) return;
+ double size_x, size_y;
- if (priv->twinview != TV_NONE && priv->screen_no == -1 && (priv->flags & ABSOLUTE_FLAG))
- {
- if (priv->twinview == TV_LEFT_RIGHT)
- {
- tabletSize = priv->bottomX - priv->tvoffsetX;
- if (v0 > tabletSize && v0 <= priv->bottomX)
- priv->currentScreen = 1;
- if (v0 > priv->topX && v0 <= priv->topX + priv->tvoffsetX)
- priv->currentScreen = 0;
- }
- if (priv->twinview == TV_ABOVE_BELOW)
- {
- tabletSize = priv->bottomY - priv->tvoffsetY;
- if (v0 > tabletSize && v0 <= priv->bottomY)
- priv->currentScreen = 1;
- if (v0 > priv->topY && v0 <= priv->topY + priv->tvoffsetY)
- priv->currentScreen = 0;
- }
- if (priv->twinview == TV_RIGHT_LEFT)
- {
- tabletSize = priv->bottomX - priv->tvoffsetX;
- if (v0 > tabletSize && v0 <= priv->bottomX)
- priv->currentScreen = 0;
- if (v0 > priv->topX && v0 <= priv->topX + priv->tvoffsetX)
- priv->currentScreen = 1;
- }
- if (priv->twinview == TV_BELOW_ABOVE)
- {
- tabletSize = priv->bottomY - priv->tvoffsetY;
- if (v0 > tabletSize && v0 <= priv->bottomY)
- priv->currentScreen = 0;
- if (v0 > priv->topY && v0 <= priv->topY + priv->tvoffsetY)
- priv->currentScreen = 1;
- }
- DBG(10, priv, "TwinView setup screenToSet=%d\n",
- priv->currentScreen);
- }
-
- wcmMappingFactor(pInfo);
- if (!(priv->flags & ABSOLUTE_FLAG) || screenInfo.numScreens == 1 || !priv->wcmMMonitor)
- return;
+ DBG(10, priv, "\n"); /* just prints function name */
- v0 = v0 - priv->topX;
- v1 = v1 - priv->topY;
+ DBG(10, priv,
+ "Active tablet area x=%d y=%d map"
+ " to maxWidth =%d maxHeight =%d\n",
+ priv->bottomX, priv->bottomY,
+ priv->maxWidth, priv->maxHeight);
- if (priv->screen_no == -1)
- {
- for (i = 0; i < priv->numScreen; i++)
- {
- if (v0 * priv->factorX >= priv->screenTopX[i] &&
- v0 * priv->factorX < priv->screenBottomX[i] - 0.5)
- {
-
- for (j = 0; j < priv->numScreen; j++)
- {
- if (v1 * priv->factorY >= priv->screenTopY[j] &&
- v1 * priv->factorY <= priv->screenBottomY[j] - 0.5)
- {
- if (j == i)
- {
- screenToSet = i;
- break;
- }
- }
- }
-
- if (screenToSet != -1)
- break;
- }
- }
- }
- else
- screenToSet = priv->screen_no;
+ /* bottomX/bottomY are scaled values of maxX/maxY such that it
+ * will scale tablet to screen ratio when passed to xf86AxisScale().
+ * Use this to compute similar factor for scaling in relative
+ * mode. If screen:tablet are 1:1 ratio then no scaling.
+ */
- if (screenToSet == -1)
- {
- DBG(3, priv, "Error: "
- "Can not find valid screen (currentScreen=%d)\n",
- priv->currentScreen);
- return;
- }
+ size_x = priv->bottomX - priv->topX;
+ size_y = priv->bottomY - priv->topY;
- wcmVirtualTabletPadding(pInfo);
- x = ((double)(v0 + priv->leftPadding) * priv->factorX) - priv->screenTopX[screenToSet] + 0.5;
- y = ((double)(v1 + priv->topPadding) * priv->factorY) - priv->screenTopY[screenToSet] + 0.5;
-
- if (x >= screenInfo.screens[screenToSet]->width)
- x = screenInfo.screens[screenToSet]->width - 1;
- if (y >= screenInfo.screens[screenToSet]->height)
- y = screenInfo.screens[screenToSet]->height - 1;
-
- xf86XInputSetScreen(pInfo, screenToSet, x, y);
- DBG(10, priv, "current=%d ToSet=%d\n",
- priv->currentScreen, screenToSet);
- priv->currentScreen = screenToSet;
+ priv->factorX = size_x / priv->bottomX;
+ priv->factorY = size_y / priv->bottomY;
+ DBG(2, priv, "X factor = %.3g, Y factor = %.3g\n",
+ priv->factorX, priv->factorY);
}
/*****************************************************************************
@@ -211,84 +129,51 @@ static void wcmSetScreen(InputInfoPtr pInfo, int v0, int v1)
* previous one.
****************************************************************************/
-static void wcmSendButtons(InputInfoPtr pInfo, int buttons, int rx, int ry,
- int rz, int v3, int v4, int v5)
+static void wcmSendButtons(InputInfoPtr pInfo, int buttons,
+ int first_val, int num_vals, int *valuators)
{
- int button, mask;
+ int button, mask, first_button;
WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
WacomCommonPtr common = priv->common;
DBG(6, priv, "buttons=%d\n", buttons);
+ /* button behaviour (TPC button on):
+ if only tip is pressed/released, send button 1 events
+ if button N is pressed and tip is pressed/released, send
+ button N events.
+ if tip is already down and button N is pressed/released,
+ send button 1 release, then button N events.
+ */
+
+ first_button = 0; /* zero-indexed because of mask */
+
/* Tablet PC buttons only apply to penabled devices */
- if (common->wcmTPCButton && (priv->flags & STYLUS_ID))
+ if (common->wcmTPCButton && IsStylus(priv))
{
- if ( buttons & 1 )
- {
- if ( !(priv->flags & TPCBUTTONS_FLAG) )
- {
- priv->flags |= TPCBUTTONS_FLAG;
-
- if (buttons == 1) {
- /* Button 1 pressed */
- sendAButton(pInfo, 0, 1, rx, ry, rz, v3, v4, v5);
- } else {
- /* send all pressed buttons down */
- for (button=2; button<=WCM_MAX_BUTTONS; button++)
- {
- mask = 1 << (button-1);
- if ( buttons & mask )
- {
- /* set to the configured button */
- sendAButton(pInfo, button-1, 1, rx, ry,
- rz, v3, v4, v5);
- }
- }
- }
- }
- else
- {
- for (button=2; button<=WCM_MAX_BUTTONS; button++)
- {
- mask = 1 << (button-1);
- if ((mask & priv->oldButtons) != (mask & buttons))
- {
- /* set to the configured buttons */
- sendAButton(pInfo, button-1, mask & buttons,
- rx, ry, rz, v3, v4, v5);
- }
- }
- }
- }
- else if ( priv->flags & TPCBUTTONS_FLAG )
- {
- priv->flags &= ~TPCBUTTONS_FLAG;
+ first_button = (buttons <= 1) ? 0 : 1;
- /* send all pressed buttons up */
- for (button=1; button<=WCM_MAX_BUTTONS; button++)
- {
- mask = 1 << (button-1);
- if ((mask & priv->oldButtons) != (mask & buttons) || (mask & buttons) )
- {
- /* set to the configured button */
- sendAButton(pInfo, button-1, 0, rx, ry,
- rz, v3, v4, v5);
- }
- }
+ /* tip released? release all buttons */
+ if ((buttons & 1) == 0)
+ buttons = 0;
+ /* tip pressed? send all other button presses */
+ else if ((buttons & 1) != (priv->oldButtons & 1))
+ priv->oldButtons = 0;
+ /* other button changed while tip is still down? release tip */
+ else if ((buttons & 1) && (buttons != priv->oldButtons))
+ {
+ buttons &= ~1;
+ first_button = 0;
}
}
- else /* normal buttons */
+
+ for (button = first_button; button < WCM_MAX_BUTTONS; button++)
{
- for (button=1; button<=WCM_MAX_BUTTONS; button++)
- {
- mask = 1 << (button-1);
- if ((mask & priv->oldButtons) != (mask & buttons))
- {
- /* set to the configured button */
- sendAButton(pInfo, button-1, mask & buttons, rx, ry,
- rz, v3, v4, v5);
- }
- }
+ mask = 1 << button;
+ if ((mask & priv->oldButtons) != (mask & buttons))
+ sendAButton(pInfo, button, (mask & buttons),
+ first_val, num_vals, valuators);
}
+
}
void wcmEmitKeycode (DeviceIntPtr keydev, int keycode, int state)
@@ -296,37 +181,6 @@ void wcmEmitKeycode (DeviceIntPtr keydev, int keycode, int state)
xf86PostKeyboardEvent (keydev, keycode, state);
}
-static void toggleDisplay(InputInfoPtr pInfo)
-{
- WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
- WacomCommonPtr common = priv->common;
-
- if (priv->numScreen > 1)
- {
- if (IsPad(priv)) /* toggle display for all tools except pad */
- {
- WacomDevicePtr tmppriv;
- for (tmppriv = common->wcmDevices; tmppriv; tmppriv = tmppriv->next)
- {
- if (!IsPad(tmppriv))
- {
- int screen = tmppriv->screen_no;
- if (++screen >= tmppriv->numScreen)
- screen = -1;
- wcmChangeScreen(tmppriv->pInfo, screen);
- }
- }
- }
- else /* toggle display only for the selected tool */
- {
- int screen = priv->screen_no;
- if (++screen >= priv->numScreen)
- screen = -1;
- wcmChangeScreen(pInfo, screen);
- }
- }
-}
-
/*****************************************************************************
* countPresses
* Count the number of key/button presses not released for the given key
@@ -346,43 +200,16 @@ static int countPresses(int keybtn, unsigned int* keys, int size)
return count;
}
-/*****************************************************************************
- * sendAButton --
- * Send one button event, called by wcmSendButtons
- ****************************************************************************/
-static void sendAButton(InputInfoPtr pInfo, int button, int mask,
- int rx, int ry, int rz, int v3, int v4, int v5)
+static void sendAction(InputInfoPtr pInfo, int press,
+ unsigned int *keys, int nkeys,
+ int first_val, int num_val, int *valuators)
{
- WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
-#ifdef DEBUG
- WacomCommonPtr common = priv->common;
-#endif
- int is_absolute = priv->flags & ABSOLUTE_FLAG;
int i;
- int naxes = priv->naxes;
-
- if (!priv->button[button]) /* ignore this button event */
- return;
-
- DBG(4, priv, "TPCButton(%s) button=%d state=%d "
- "code=%08x, coreEvent=%s \n",
- common->wcmTPCButton ? "on" : "off",
- button, mask, priv->button[button],
- (priv->button[button] & AC_CORE) ? "yes" : "no");
-
- if (!priv->keys[button][0])
- {
- /* No button action configured, send button */
- xf86PostButtonEvent(pInfo->dev, is_absolute, priv->button[button], (mask != 0), 0, naxes,
- rx, ry, rz, v3, v4, v5);
- return;
- }
-
/* Actions only trigger on press, not release */
- for (i = 0; mask && i < ARRAY_SIZE(priv->keys[button]); i++)
+ for (i = 0; press && i < nkeys; i++)
{
- unsigned int action = priv->keys[button][i];
+ unsigned int action = keys[i];
if (!action)
break;
@@ -393,10 +220,10 @@ static void sendAButton(InputInfoPtr pInfo, int button, int mask,
{
int btn_no = (action & AC_CODE);
int is_press = (action & AC_KEYBTNPRESS);
- xf86PostButtonEvent(pInfo->dev,
- is_absolute, btn_no,
- is_press, 0, naxes,
- rx, ry, rz, v3, v4, v5);
+ xf86PostButtonEventP(pInfo->dev,
+ is_absolute(pInfo), btn_no,
+ is_press, first_val, num_val,
+ VCOPY(valuators, num_val));
}
break;
case AC_KEY:
@@ -407,32 +234,17 @@ static void sendAButton(InputInfoPtr pInfo, int button, int mask,
}
break;
case AC_MODETOGGLE:
- if (mask)
+ if (press)
wcmDevSwitchModeCall(pInfo,
- (is_absolute) ? Relative : Absolute); /* not a typo! */
- break;
- /* FIXME: this should be implemented as 4 values,
- * there's no reason to have a DBLCLICK */
- case AC_DBLCLICK:
- xf86PostButtonEvent(pInfo->dev, is_absolute,
- 1,1,0,naxes, rx,ry,rz,v3,v4,v5);
- xf86PostButtonEvent(pInfo->dev, is_absolute,
- 1,0,0,naxes,rx,ry,rz,v3,v4,v5);
- xf86PostButtonEvent(pInfo->dev, is_absolute,
- 1,1,0,naxes, rx,ry,rz,v3,v4,v5);
- xf86PostButtonEvent(pInfo->dev, is_absolute,
- 1,0,0,naxes,rx,ry,rz,v3,v4,v5);
- break;
- case AC_DISPLAYTOGGLE:
- toggleDisplay(pInfo);
+ (is_absolute(pInfo)) ? Relative : Absolute); /* not a typo! */
break;
}
}
/* Release all non-released keys for this button. */
- for (i = 0; !mask && i < ARRAY_SIZE(priv->keys[button]); i++)
+ for (i = 0; !press && i < nkeys; i++)
{
- unsigned int action = priv->keys[button][i];
+ unsigned int action = keys[i];
switch ((action & AC_TYPE))
{
@@ -444,12 +256,11 @@ static void sendAButton(InputInfoPtr pInfo, int button, int mask,
if (!(action & AC_KEYBTNPRESS))
break;
- if (countPresses(btn_no, &priv->keys[button][i],
- ARRAY_SIZE(priv->keys[button]) - i))
- xf86PostButtonEvent(pInfo->dev,
- is_absolute, btn_no,
- 0, 0, naxes,
- rx, ry, rz, v3, v4, v5);
+ if (countPresses(btn_no, &keys[i], nkeys - i))
+ xf86PostButtonEventP(pInfo->dev,
+ is_absolute(pInfo), btn_no,
+ 0, first_val, num_val,
+ VCOPY(valuators, num_val));
}
break;
case AC_KEY:
@@ -460,8 +271,7 @@ static void sendAButton(InputInfoPtr pInfo, int button, int mask,
if (!(action & AC_KEYBTNPRESS))
break;
- if (countPresses(key_code, &priv->keys[button][i],
- ARRAY_SIZE(priv->keys[button]) - i))
+ if (countPresses(key_code, &keys[i], nkeys - i))
wcmEmitKeycode(pInfo->dev, key_code, 0);
}
}
@@ -470,130 +280,206 @@ static void sendAButton(InputInfoPtr pInfo, int button, int mask,
}
/*****************************************************************************
- * sendWheelStripEvents --
- * Send events defined for relative/absolute wheels or strips
+ * sendAButton --
+ * Send one button event, called by wcmSendButtons
****************************************************************************/
-
-static void sendWheelStripEvents(InputInfoPtr pInfo, const WacomDeviceState* ds,
- int x, int y, int z, int v3, int v4, int v5)
+static void sendAButton(InputInfoPtr pInfo, int button, int mask,
+ int first_val, int num_val, int *valuators)
{
WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
- int fakeButton = 0, i, value = 0, naxes = priv->naxes;
- unsigned *keyP = 0;
- int is_absolute = priv->flags & ABSOLUTE_FLAG;
+#ifdef DEBUG
+ WacomCommonPtr common = priv->common;
+#endif
+ int mapped_button;
- DBG(10, priv, "\n");
+ if (!priv->button[button]) /* ignore this button event */
+ return;
- /* emulate events for relative wheel */
- if ( ds->relwheel )
+ mapped_button = priv->button[button];
+
+ DBG(4, priv, "TPCButton(%s) button=%d state=%d "
+ "mapped_button=%d, coreEvent=%s \n",
+ common->wcmTPCButton ? "on" : "off",
+ button, mask, mapped_button,
+ (mapped_button & AC_CORE) ? "yes" : "no");
+
+ if (!priv->keys[mapped_button][0])
{
- value = ds->relwheel;
- if ( ds->relwheel > 0 )
- {
- fakeButton = priv->relup;
- keyP = priv->rupk;
- }
- else
- {
- fakeButton = priv->reldn;
- keyP = priv->rdnk;
- }
+ /* No button action configured, send button */
+ xf86PostButtonEventP(pInfo->dev, is_absolute(pInfo),
+ mapped_button, (mask != 0),
+ first_val, num_val,
+ VCOPY(valuators, num_val));
+ return;
}
- /* emulate events for absolute wheel when it is a touch ring (on pad) */
- if ( (ds->abswheel != priv->oldWheel) && IsPad(priv) )
+ sendAction(pInfo, (mask != 0), priv->keys[mapped_button],
+ ARRAY_SIZE(priv->keys[mapped_button]),
+ first_val, num_val, valuators);
+}
+
+/**
+ * Get the distance an axis was scrolled. This function is aware
+ * of the different ways different scrolling axes work and strives
+ * to produce a common representation of relative change.
+ *
+ * @param current Current value of the axis
+ * @param old Previous value of the axis
+ * @param wrap Maximum value before wraparound occurs (0 if axis does not wrap)
+ * @param flags Flags defining axis attributes: AXIS_INVERT and AXIS_BITWISE
+ * @return Relative change in axis value
+ */
+static int getScrollDelta(int current, int old, int wrap, int flags)
+{
+ int delta;
+
+ if (flags & AXIS_BITWISE)
{
- value = priv->oldWheel - ds->abswheel;
- if ( value > 0 )
- {
- fakeButton = priv->wheelup;
- keyP = priv->wupk;
- }
+ current = (int)log2((current << 1) | 0x01);
+ old = (int)log2((old << 1) | 0x01);
+ wrap = (int)log2((wrap << 1) | 0x01);
+ }
+
+ delta = current - old;
+
+ if (flags & AXIS_INVERT)
+ delta = -delta;
+
+ if (wrap != 0)
+ {
+ /* Wraparound detection. If the distance old..current
+ * is larger than the old..current considering the
+ * wraparound, assume wraparound and readjust */
+ int wrap_delta;
+
+ if (delta < 0)
+ wrap_delta = (wrap + 1) + delta;
else
- {
- fakeButton = priv->wheeldn;
- keyP = priv->wdnk;
- }
+ wrap_delta = -((wrap + 1) - delta);
+
+ if (abs(wrap_delta) < abs(delta))
+ delta = wrap_delta;
}
- /* emulate events for left strip */
- if ( ds->stripx != priv->oldStripX )
+ return delta;
+}
+
+/**
+ * Get the scroll button/action to send given the delta of
+ * the scrolling axis and the possible events that can be
+ * sent.
+ *
+ * @param delta Amount of change in the scrolling axis
+ * @param button_up Button event to send on scroll up
+ * @param button_dn Button event to send on scroll down
+ * @param action_up Action to send on scroll up
+ * @param action_dn Action to send on scroll down
+ * @param[out] action Action that should be performed
+ * @return Button that should be pressed
+ */
+static int getWheelButton(int delta, int button_up, int button_dn,
+ unsigned int *action_up, unsigned int *action_dn,
+ unsigned int **action)
+{
+ int button = 0;
+ *action = NULL;
+
+ if (delta)
{
- int temp = 0, n;
- for (i=1; i<14; i++)
- {
- n = 1 << (i-1);
- if ( ds->stripx & n )
- temp = i;
- if ( priv->oldStripX & n )
- value = i;
- if ( temp & value) break;
- }
+ button = delta > 0 ? button_up : button_dn;
+ *action = delta > 0 ? action_up : action_dn;
+ }
- value -= temp;
- if ( value > 0 )
- {
- fakeButton = priv->striplup;
- keyP = priv->slupk;
- }
- else if ( value < 0 )
- {
- fakeButton = priv->stripldn;
- keyP = priv->sldnk;
- }
+ return button;
+}
+
+/**
+ * Send button or actions for a scrolling axis.
+ *
+ * @param button X button number to send if no action is defined
+ * @param action Action to send
+ * @param pInfo
+ * @param first_val
+ * @param num_vals
+ * @param valuators
+ */
+static void sendWheelStripEvent(int button, unsigned int *action, InputInfoPtr pInfo,
+ int first_val, int num_vals, int *valuators)
+{
+ WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+
+ unsigned int button_action[1] = {button | AC_BUTTON | AC_KEYBTNPRESS};
+ if (!action || !(*action)) {
+ DBG(10, priv, "No wheel/strip action set; sending button %d (action %d).\n", button, button_action[0]);
+ action = &button_action[0];
}
- /* emulate events for right strip */
- if ( ds->stripy != priv->oldStripY )
+ sendAction(pInfo, 1, action, ARRAY_SIZE(action), first_val, num_vals, valuators);
+ sendAction(pInfo, 0, action, ARRAY_SIZE(action), first_val, num_vals, valuators);
+}
+
+/*****************************************************************************
+ * sendWheelStripEvents --
+ * Send events defined for relative/absolute wheels or strips
+ ****************************************************************************/
+
+static void sendWheelStripEvents(InputInfoPtr pInfo, const WacomDeviceState* ds,
+ int first_val, int num_vals, int *valuators)
+{
+ WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+ int fakeButton = 0, delta = 0;
+ unsigned int *fakeKey = NULL;
+
+ DBG(10, priv, "\n");
+
+ /* emulate events for left strip */
+ delta = getScrollDelta(ds->stripx, priv->oldStripX, 0, AXIS_INVERT | AXIS_BITWISE);
+ if (delta && IsPad(priv) && priv->oldProximity == ds->proximity)
{
- int temp = 0, n;
- for (i=1; i<14; i++)
- {
- n = 1 << (i-1);
- if ( ds->stripy & n )
- temp = i;
- if ( priv->oldStripY & n )
- value = i;
- if ( temp & value) break;
- }
+ DBG(10, priv, "Left touch strip scroll delta = %d\n", delta);
+ fakeButton = getWheelButton(delta, priv->striplup, priv->stripldn,
+ priv->strip_keys[0+1], priv->strip_keys[1+1], &fakeKey);
+ sendWheelStripEvent(fakeButton, fakeKey, pInfo, first_val, num_vals, valuators);
+ }
- value -= temp;
- if ( value > 0 )
- {
- fakeButton = priv->striprup;
- keyP = priv->srupk;
- }
- else if ( value < 0 )
- {
- fakeButton = priv->striprdn;
- keyP = priv->srdnk;
- }
+ /* emulate events for right strip */
+ delta = getScrollDelta(ds->stripy, priv->oldStripY, 0, AXIS_INVERT | AXIS_BITWISE);
+ if (delta && IsPad(priv) && priv->oldProximity == ds->proximity)
+ {
+ DBG(10, priv, "Right touch strip scroll delta = %d\n", delta);
+ fakeButton = getWheelButton(delta, priv->striprup, priv->striprdn,
+ priv->strip_keys[2+1], priv->strip_keys[3+1], &fakeKey);
+ sendWheelStripEvent(fakeButton, fakeKey, pInfo, first_val, num_vals, valuators);
}
- if (!fakeButton) return;
+ /* emulate events for relative wheel */
+ delta = getScrollDelta(ds->relwheel, 0, 0, 0);
+ if (delta && IsCursor(priv) && priv->oldProximity == ds->proximity)
+ {
+ DBG(10, priv, "Relative wheel scroll delta = %d\n", delta);
+ fakeButton = getWheelButton(delta, priv->relup, priv->reldn,
+ priv->wheel_keys[0+1], priv->wheel_keys[1+1], &fakeKey);
+ sendWheelStripEvent(fakeButton, fakeKey, pInfo, first_val, num_vals, valuators);
+ }
- DBG(10, priv, "send fakeButton %x with value = %d \n",
- fakeButton, value);
+ /* emulate events for left touch ring */
+ delta = getScrollDelta(ds->abswheel, priv->oldWheel, MAX_PAD_RING, AXIS_INVERT);
+ if (delta && IsPad(priv) && priv->oldProximity == ds->proximity)
+ {
+ DBG(10, priv, "Left touch wheel scroll delta = %d\n", delta);
+ fakeButton = getWheelButton(delta, priv->wheelup, priv->wheeldn,
+ priv->wheel_keys[2+1], priv->wheel_keys[3+1], &fakeKey);
+ sendWheelStripEvent(fakeButton, fakeKey, pInfo, first_val, num_vals, valuators);
+ }
- switch (fakeButton & AC_TYPE)
+ /* emulate events for right touch ring */
+ delta = getScrollDelta(ds->abswheel2, priv->oldWheel2, MAX_PAD_RING, AXIS_INVERT);
+ if (delta && IsPad(priv) && priv->oldProximity == ds->proximity)
{
- case 0: /* no spec. action defined */
- case AC_BUTTON:
- /* send both button on/off in the same event for pad */
- xf86PostButtonEvent(pInfo->dev, is_absolute, fakeButton & AC_CODE,
- 1,0,naxes,x,y,z,v3,v4,v5);
-
- xf86PostButtonEvent(pInfo->dev, is_absolute, fakeButton & AC_CODE,
- 0,0,naxes,x,y,z,v3,v4,v5);
- break;
-
- case AC_KEY:
- wcmEmitKeycode(pInfo->dev, (fakeButton & AC_CODE), 1);
- wcmEmitKeycode(pInfo->dev, (fakeButton & AC_CODE), 0);
- break;
-
- default:
- xf86Msg(X_WARNING, "%s: unsupported event %x \n", pInfo->name, fakeButton);
+ DBG(10, priv, "Right touch wheel scroll delta = %d\n", delta);
+ fakeButton = getWheelButton(delta, priv->wheel2up, priv->wheel2dn,
+ priv->wheel_keys[4+1], priv->wheel_keys[5+1], &fakeKey);
+ sendWheelStripEvent(fakeButton, fakeKey, pInfo, first_val, num_vals, valuators);
}
}
@@ -602,49 +488,222 @@ static void sendWheelStripEvents(InputInfoPtr pInfo, const WacomDeviceState* ds,
* Send events common between pad and stylus/cursor/eraser.
****************************************************************************/
-static void sendCommonEvents(InputInfoPtr pInfo, const WacomDeviceState* ds, int x, int y, int z, int v3, int v4, int v5)
+static void sendCommonEvents(InputInfoPtr pInfo, const WacomDeviceState* ds,
+ int first_val, int num_vals, int *valuators)
{
WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
int buttons = ds->buttons;
/* send button events when state changed or first time in prox and button unpresses */
if (priv->oldButtons != buttons || (!priv->oldProximity && !buttons))
- wcmSendButtons(pInfo,buttons,x,y,z,v3,v4,v5);
+ wcmSendButtons(pInfo,buttons, first_val, num_vals, valuators);
/* emulate wheel/strip events when defined */
- if ( ds->relwheel || ds->abswheel ||
+ if ( ds->relwheel || (ds->abswheel != priv->oldWheel) || (ds->abswheel2 != priv->oldWheel2) ||
( (ds->stripx - priv->oldStripX) && ds->stripx && priv->oldStripX) ||
((ds->stripy - priv->oldStripY) && ds->stripy && priv->oldStripY) )
- sendWheelStripEvents(pInfo, ds, x, y, z, v3, v4, v5);
+ sendWheelStripEvents(pInfo, ds, first_val, num_vals, valuators);
}
/* rotate x and y before post X inout events */
-void wcmRotateCoordinates(InputInfoPtr pInfo, int* x, int* y)
+void wcmRotateAndScaleCoordinates(InputInfoPtr pInfo, int* x, int* y)
{
WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
WacomCommonPtr common = priv->common;
+ DeviceIntPtr dev = pInfo->dev;
+ AxisInfoPtr axis_x, axis_y;
int tmp_coord;
- /* rotation mixes x and y up a bit */
- if (common->wcmRotate == ROTATE_CW)
+ /* scale into on topX/topY area */
+ axis_x = &dev->valuator->axes[0];
+ axis_y = &dev->valuator->axes[1];
+
+ /* Don't try to scale relative axes */
+ if (axis_x->max_value > axis_x->min_value)
+ *x = xf86ScaleAxis(*x, axis_x->max_value, axis_x->min_value,
+ priv->bottomX, priv->topX);
+
+ if (axis_y->max_value > axis_y->min_value)
+ *y = xf86ScaleAxis(*y, axis_y->max_value, axis_y->min_value,
+ priv->bottomY, priv->topY);
+
+ /* coordinates are now in the axis rage we advertise for the device */
+
+ if (common->wcmRotate == ROTATE_CW || common->wcmRotate == ROTATE_CCW)
{
tmp_coord = *x;
- *x = *y;
- *y = priv->maxY - tmp_coord;
+
+ *x = xf86ScaleAxis(*y,
+ axis_x->max_value, axis_x->min_value,
+ axis_y->max_value, axis_y->min_value);
+ *y = xf86ScaleAxis(tmp_coord,
+ axis_y->max_value, axis_y->min_value,
+ axis_x->max_value, axis_x->min_value);
}
+
+ if (common->wcmRotate == ROTATE_CW)
+ *y = axis_y->max_value - (*y - axis_y->min_value);
else if (common->wcmRotate == ROTATE_CCW)
+ *x = axis_x->max_value - (*x - axis_x->min_value);
+ else if (common->wcmRotate == ROTATE_HALF)
{
- tmp_coord = *y;
- *y = *x;
- *x = priv->maxX - tmp_coord;
+ *x = axis_x->max_value - (*x - axis_x->min_value);
+ *y = axis_y->max_value - (*y - axis_y->min_value);
}
- else if (common->wcmRotate == ROTATE_HALF)
+
+
+ DBG(10, priv, "rotate/scaled to %d/%d\n", *x, *y);
+}
+
+static void wcmUpdateOldState(const InputInfoPtr pInfo,
+ const WacomDeviceState *ds)
+{
+ const WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+ int tx, ty;
+
+ priv->oldWheel = ds->abswheel;
+ priv->oldWheel2 = ds->abswheel2;
+ priv->oldButtons = ds->buttons;
+
+ if (IsPad(priv))
+ {
+ tx = ds->stripx;
+ ty = ds->stripy;
+ } else
+ {
+ tx = ds->tiltx;
+ ty = ds->tilty;
+ }
+
+ priv->oldX = priv->currentX;
+ priv->oldY = priv->currentY;
+ priv->oldZ = ds->pressure;
+ priv->oldTiltX = tx;
+ priv->oldTiltY = ty;
+ priv->oldStripX = ds->stripx;
+ priv->oldStripY = ds->stripy;
+ priv->oldRot = ds->rotation;
+ priv->oldThrottle = ds->throttle;
+}
+
+static void
+wcmSendPadEvents(InputInfoPtr pInfo, const WacomDeviceState* ds,
+ int first_val, int num_vals, int *valuators)
+{
+ int i;
+ WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+
+ if (!priv->oldProximity && ds->proximity)
+ xf86PostProximityEventP(pInfo->dev, 1, first_val, num_vals, VCOPY(valuators, num_vals));
+
+ for (i = 0; i < num_vals; i++)
+ if (valuators[i])
+ break;
+ if (i < num_vals || ds->buttons || ds->relwheel ||
+ (ds->abswheel != priv->oldWheel) || (ds->abswheel2 != priv->oldWheel2))
+ {
+ sendCommonEvents(pInfo, ds, first_val, num_vals, valuators);
+
+ /* xf86PostMotionEvent is only needed to post the valuators
+ * It should NOT move the cursor.
+ */
+ xf86PostMotionEventP(pInfo->dev, TRUE, first_val, num_vals,
+ VCOPY(valuators, num_vals));
+ }
+ else
+ {
+ if (priv->oldButtons)
+ wcmSendButtons(pInfo, ds->buttons, first_val, num_vals, valuators);
+ }
+
+ if (priv->oldProximity && !ds->proximity)
+ xf86PostProximityEventP(pInfo->dev, 0, first_val, num_vals,
+ VCOPY(valuators, num_vals));
+}
+
+/* Send events for all tools but pads */
+static void
+wcmSendNonPadEvents(InputInfoPtr pInfo, const WacomDeviceState *ds,
+ int first_val, int num_vals, int *valuators)
+{
+ WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+
+ if (!is_absolute(pInfo))
+ {
+ valuators[0] -= priv->oldX;
+ valuators[1] -= priv->oldY;
+ valuators[2] -= priv->oldZ;
+ if (IsCursor(priv))
+ {
+ valuators[3] -= priv->oldRot;
+ valuators[4] -= priv->oldThrottle;
+ } else
+ {
+ valuators[3] -= priv->oldTiltX;
+ valuators[4] -= priv->oldTiltY;
+ }
+ valuators[5] -= priv->oldWheel;
+ valuators[6] -= priv->oldWheel2;
+ }
+
+ /* coordinates are ready we can send events */
+ if (ds->proximity)
{
- *x = priv->maxX - *x;
- *y = priv->maxY - *y;
+ /* unify acceleration in both directions
+ * for relative mode to draw a circle
+ */
+ if (!is_absolute(pInfo))
+ valuators[0] *= priv->factorY / priv->factorX;
+ else
+ {
+ /* Padding virtual values */
+ wcmVirtualTabletPadding(pInfo);
+ valuators[0] += priv->leftPadding;
+ valuators[1] += priv->topPadding;
+ }
+
+ /* don't emit proximity events if device does not support proximity */
+ if ((pInfo->dev->proximity && !priv->oldProximity))
+ xf86PostProximityEventP(pInfo->dev, 1, first_val, num_vals,
+ VCOPY(valuators, num_vals));
+
+ /* Move the cursor to where it should be before sending button events */
+ if(!(priv->flags & BUTTONS_ONLY_FLAG))
+ {
+ xf86PostMotionEventP(pInfo->dev, is_absolute(pInfo),
+ first_val, num_vals,
+ VCOPY(valuators, num_vals));
+ /* For relative events, do not repost
+ * the valuators. Otherwise, a button
+ * event in sendCommonEvents will move the
+ * axes again.
+ */
+ if (!is_absolute(pInfo))
+ {
+ first_val = 0;
+ num_vals = 0;
+ }
+ }
+
+ sendCommonEvents(pInfo, ds, first_val, num_vals, valuators);
}
+ else /* not in proximity */
+ {
+ int buttons = 0;
+
+ /* reports button up when the device has been
+ * down and becomes out of proximity */
+ if (priv->oldButtons)
+ wcmSendButtons(pInfo, buttons, first_val, num_vals, valuators);
+
+ if (priv->oldProximity)
+ xf86PostProximityEventP(pInfo->dev, 0, first_val, num_vals,
+ VCOPY(valuators, num_vals));
+ } /* not in proximity */
}
+#define IsArtPen(ds) (ds->device_id == 0x885 || ds->device_id == 0x804 || ds->device_id == 0x100804)
+
/*****************************************************************************
* wcmSendEvents --
* Send events according to the device state.
@@ -657,31 +716,27 @@ void wcmSendEvents(InputInfoPtr pInfo, const WacomDeviceState* ds)
#endif
int type = ds->device_type;
int id = ds->device_id;
- int serial = (int)ds->serial_num;
- int is_proximity = ds->proximity;
+ unsigned int serial = ds->serial_num;
int x = ds->x;
int y = ds->y;
int z = ds->pressure;
- int buttons = ds->buttons;
int tx = ds->tiltx;
int ty = ds->tilty;
- int rot = ds->rotation;
- int throttle = ds->throttle;
- int wheel = ds->abswheel;
WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
- WacomCommonPtr common = priv->common;
- int naxes = priv->naxes;
- int is_absolute = priv->flags & ABSOLUTE_FLAG;
- int v3, v4, v5;
+ int v3, v4, v5, v6;
+ int valuators[priv->naxes];
if (priv->serial && serial != priv->serial)
{
DBG(10, priv, "serial number"
- " is %u but your system configured %u",
- serial, (int)priv->serial);
+ " is %u but your system configured %u",
+ serial, (int)priv->serial);
return;
}
+ if (priv->cur_serial != serial)
+ wcmUpdateSerial(pInfo, serial);
+
/* don't move the cursor when going out-prox */
if (!ds->proximity)
{
@@ -697,36 +752,44 @@ void wcmSendEvents(InputInfoPtr pInfo, const WacomDeviceState* ds)
}
DBG(7, priv, "[%s] o_prox=%s x=%d y=%d z=%d "
- "b=%s b=%d tx=%d ty=%d wl=%d rot=%d th=%d\n",
- (type == STYLUS_ID) ? "stylus" :
- (type == CURSOR_ID) ? "cursor" :
- (type == ERASER_ID) ? "eraser" :
- (type == TOUCH_ID) ? "touch" : "pad",
+ "b=%s b=%d tx=%d ty=%d wl=%d wl2=%d rot=%d th=%d\n",
+ pInfo->type_name,
priv->oldProximity ? "true" : "false",
- x, y, z, is_button ? "true" : "false", buttons,
- tx, ty, wheel, rot, throttle);
+ x, y, z, is_button ? "true" : "false", ds->buttons,
+ tx, ty, ds->abswheel, ds->abswheel2, ds->rotation, ds->throttle);
- wcmRotateCoordinates(pInfo, &x, &y);
+ if (ds->proximity)
+ wcmRotateAndScaleCoordinates(pInfo, &x, &y);
- if (IsCursor(priv))
+ if (IsCursor(priv))
{
- v3 = rot;
- v4 = throttle;
+ v3 = ds->rotation;
+ v4 = ds->throttle;
}
else /* Intuos styli have tilt */
{
v3 = tx;
v4 = ty;
}
- v5 = wheel;
+
+ v5 = ds->abswheel;
+ v6 = ds->abswheel2;
+ if (IsStylus(priv) && !IsArtPen(ds))
+ {
+ /* Normalize abswheel airbrush data to Art Pen rotation range.
+ * We do not normalize Art Pen. They are already at the range.
+ */
+ v5 = ds->abswheel * MAX_ROTATION_RANGE/
+ (double)MAX_ABS_WHEEL + MIN_ROTATION;
+ }
DBG(6, priv, "%s prox=%d\tx=%d"
- "\ty=%d\tz=%d\tv3=%d\tv4=%d\tv5=%d\tid=%d"
+ "\ty=%d\tz=%d\tv3=%d\tv4=%d\tv5=%d\tv6=%d\tid=%d"
"\tserial=%u\tbutton=%s\tbuttons=%d\n",
- is_absolute ? "abs" : "rel",
- is_proximity,
- x, y, z, v3, v4, v5, id, serial,
- is_button ? "true" : "false", buttons);
+ is_absolute(pInfo) ? "abs" : "rel",
+ ds->proximity,
+ x, y, z, v3, v4, v5, v6, id, serial,
+ is_button ? "true" : "false", ds->buttons);
priv->currentX = x;
priv->currentY = y;
@@ -734,197 +797,37 @@ void wcmSendEvents(InputInfoPtr pInfo, const WacomDeviceState* ds)
/* update the old records */
if(!priv->oldProximity)
{
- priv->oldWheel = wheel;
- priv->oldX = priv->currentX;
- priv->oldY = priv->currentY;
- priv->oldZ = ds->pressure;
- priv->oldTiltX = tx;
- priv->oldTiltY = ty;
- priv->oldCapacity = ds->capacity;
- priv->oldStripX = ds->stripx;
- priv->oldStripY = ds->stripy;
- priv->oldRot = rot;
- priv->oldThrottle = throttle;
+ wcmUpdateOldState(pInfo, ds);
priv->oldButtons = 0;
}
- if (!is_absolute)
- {
- x -= priv->oldX;
- y -= priv->oldY;
- z -= priv->oldZ;
- }
-
- if (type != PAD_ID)
- {
- /* coordinates are ready we can send events */
- if (is_proximity)
- {
- /* for multiple monitor support, we need to set the proper
- * screen and modify the axes before posting events */
- if(!(priv->flags & BUTTONS_ONLY_FLAG))
- {
- wcmSetScreen(pInfo, x, y);
- }
-
- /* unify acceleration in both directions
- * for relative mode to draw a circle
- */
- if (!is_absolute)
- x *= priv->factorY / priv->factorX;
- else
- {
- /* Padding virtual values */
- wcmVirtualTabletPadding(pInfo);
- x += priv->leftPadding;
- y += priv->topPadding;
- }
-
- if (common->wcmScaling)
- {
- /* In the case that wcmDevConvert doesn't get called.
- * The +-0.4 is to increase the sensitivity in relative mode.
- * Must be sensitive to which way the tool is moved or one way
- * will get a severe penalty for small movements.
- */
- if(is_absolute) {
- x -= priv->topX;
- y -= priv->topY;
- if (priv->currentScreen == 1 && priv->twinview != TV_NONE)
- {
- x -= priv->tvoffsetX;
- y -= priv->tvoffsetY;
- }
- }
- x = (int)((double)x * priv->factorX + (x>=0?0.4:-0.4));
- y = (int)((double)y * priv->factorY + (y>=0?0.4:-0.4));
-
- if ((priv->flags & ABSOLUTE_FLAG) && (priv->twinview == TV_NONE))
- {
- x -= priv->screenTopX[priv->currentScreen];
- y -= priv->screenTopY[priv->currentScreen];
- }
-
- if (priv->screen_no != -1)
- {
- if (x > priv->screenBottomX[priv->currentScreen] - priv->screenTopX[priv->currentScreen])
- x = priv->screenBottomX[priv->currentScreen];
- if (x < 0) x = 0;
- if (y > priv->screenBottomY[priv->currentScreen] - priv->screenTopY[priv->currentScreen])
- y = priv->screenBottomY[priv->currentScreen];
- if (y < 0) y = 0;
-
- }
- priv->currentSX = x;
- priv->currentSY = y;
- }
-
- /* don't emit proximity events if device does not support proximity */
- if ((pInfo->dev->proximity && !priv->oldProximity))
- xf86PostProximityEvent(pInfo->dev, 1, 0, naxes, x, y, z, v3, v4, v5);
-
- /* Move the cursor to where it should be before sending button events */
- if(!(priv->flags & BUTTONS_ONLY_FLAG))
- {
- xf86PostMotionEvent(pInfo->dev, is_absolute,
- 0, naxes, x, y, z, v3, v4, v5);
- /* For relative events, reset the axes as
- * we've already moved the device by the
- * relative amount. Otherwise, a button
- * event in sendCommonEvents will move the
- * axes again.
- */
- if (!is_absolute)
- {
- x = y = z = 0;
- v3 = v4 = v5 = 0;
- }
- }
-
- sendCommonEvents(pInfo, ds, x, y, z, v3, v4, v5);
- }
-
- /* not in proximity */
- else
- {
- buttons = 0;
-
- if (common->wcmScaling)
- {
- /* In the case that wcmDevConvert doesn't called */
- x = priv->currentSX;
- y = priv->currentSY;
- }
- /* reports button up when the device has been
- * down and becomes out of proximity */
- if (priv->oldButtons)
- wcmSendButtons(pInfo,0,x,y,z,v3,v4,v5);
+ valuators[0] = x;
+ valuators[1] = y;
+ valuators[2] = z;
+ valuators[3] = v3;
+ valuators[4] = v4;
+ valuators[5] = v5;
+ valuators[6] = v6;
- if (priv->oldProximity && pInfo->dev->proximity)
- xf86PostProximityEvent(pInfo->dev,0,0,naxes,x,y,z,v3,v4,v5);
- } /* not in proximity */
- }
- else
- {
- if (v3 || v4 || v5 || buttons || ds->relwheel)
- {
- x = 0;
- y = 0;
- if ( v3 || v4 || v5 )
- wcmSetScreen(pInfo, x, y);
-
- /* don't emit proximity events if device does not support proximity */
- if ((pInfo->dev->proximity && !priv->oldProximity))
- xf86PostProximityEvent(pInfo->dev, 1, 0, naxes, x, y, z, v3, v4, v5);
-
- sendCommonEvents(pInfo, ds, x, y, z, v3, v4, v5);
- is_proximity = 1;
- /* xf86PostMotionEvent is only needed to post the valuators
- * It should NOT move the cursor.
- */
- if ( v3 || v4 || v5 )
- {
- xf86PostMotionEvent(pInfo->dev, is_absolute,
- 0, naxes, x, y, z, v3, v4, v5);
- }
- }
- else
- {
- if (priv->oldButtons)
- wcmSendButtons(pInfo, buttons,
- x, y, z, v3, v4, v5);
- if (priv->oldProximity && pInfo->dev->proximity)
- xf86PostProximityEvent(pInfo->dev, 0, 0, naxes,
- x, y, z, v3, v4, v5);
- is_proximity = 0;
- }
- }
- priv->oldProximity = is_proximity;
- priv->old_device_id = id;
- priv->old_serial = serial;
- if (is_proximity)
- {
- priv->oldButtons = buttons;
- priv->oldWheel = wheel;
- priv->oldX = priv->currentX;
- priv->oldY = priv->currentY;
- priv->oldZ = ds->pressure;
- priv->oldCapacity = ds->capacity;
- priv->oldTiltX = tx;
- priv->oldTiltY = ty;
- priv->oldStripX = ds->stripx;
- priv->oldStripY = ds->stripy;
- priv->oldRot = rot;
- priv->oldThrottle = throttle;
+ if (type == PAD_ID)
+ wcmSendPadEvents(pInfo, ds, 3, priv->naxes - 3, &valuators[3]); /* pad doesn't post x/y/z */
+ else {
+ /* don't move the cursor if in gesture mode (except drag mode) */
+ if ((type != TOUCH_ID) || wcmTouchNeedSendEvents(priv->common))
+ wcmSendNonPadEvents(pInfo, ds, 0, priv->naxes, valuators);
}
+
+ priv->oldProximity = ds->proximity;
+ if (ds->proximity)
+ wcmUpdateOldState(pInfo, ds);
else
{
priv->oldButtons = 0;
- priv->oldWheel = 0;
+ priv->oldWheel = MAX_PAD_RING + 1;
+ priv->oldWheel2 = MAX_PAD_RING + 1;
priv->oldX = 0;
priv->oldY = 0;
priv->oldZ = 0;
- priv->oldCapacity = ds->capacity;
priv->oldTiltX = 0;
priv->oldTiltY = 0;
priv->oldStripX = 0;
@@ -932,46 +835,74 @@ void wcmSendEvents(InputInfoPtr pInfo, const WacomDeviceState* ds)
priv->oldRot = 0;
priv->oldThrottle = 0;
priv->devReverseCount = 0;
+ priv->old_serial = serial;
+ priv->old_device_id = id;
+ wcmUpdateSerial(pInfo, 0);
}
}
-/*****************************************************************************
- * wcmCheckSuppress --
- * Determine whether device state has changed enough - return 0
- * if not.
- ****************************************************************************/
-
-static int wcmCheckSuppress(WacomCommonPtr common, const WacomDeviceState* dsOrig,
- WacomDeviceState* dsNew)
+/**
+ * Determine whether device state has changed enough to warrant further
+ * processing. The driver's "suppress" setting decides how much
+ * movement/state change must occur before we process events to avoid
+ * overloading the server with minimal changes (and getting fuzzy events).
+ * wcmCheckSuppress ensures that events meet this standard.
+ *
+ * @param dsOrig Previous device state
+ * @param dsNew Current device state
+ *
+ * @retval SUPPRESS_ALL Ignore this event completely.
+ * @retval SUPPRESS_NONE Process event normally.
+ * @retval SUPPRESS_NON_MOTION Suppress all data but motion data.
+ */
+static enum WacomSuppressMode
+wcmCheckSuppress(WacomCommonPtr common,
+ const WacomDeviceState* dsOrig,
+ WacomDeviceState* dsNew)
{
int suppress = common->wcmSuppress;
- /* NOTE: Suppression value of zero disables suppression. */
- int returnV = 0;
-
- if (dsOrig->buttons != dsNew->buttons) returnV = 1;
- if (dsOrig->proximity != dsNew->proximity) returnV = 1;
- if (dsOrig->stripx != dsNew->stripx) returnV = 1;
- if (dsOrig->stripy != dsNew->stripy) returnV = 1;
- if (ABS(dsOrig->tiltx - dsNew->tiltx) > suppress) returnV = 1;
- if (ABS(dsOrig->tilty - dsNew->tilty) > suppress) returnV = 1;
- if (ABS(dsOrig->pressure - dsNew->pressure) > suppress) returnV = 1;
- if (ABS(dsOrig->capacity - dsNew->capacity) > suppress) returnV = 1;
- if (ABS(dsOrig->throttle - dsNew->throttle) > suppress) returnV = 1;
- if (ABS(dsOrig->rotation - dsNew->rotation) > suppress &&
- (1800 - ABS(dsOrig->rotation - dsNew->rotation)) > suppress) returnV = 1;
+ enum WacomSuppressMode returnV = SUPPRESS_NONE;
+
+ /* Ignore all other changes that occur after initial out-of-prox. */
+ if (!dsNew->proximity && !dsOrig->proximity)
+ return SUPPRESS_ALL;
+
+ /* Never ignore proximity changes. */
+ if (dsOrig->proximity != dsNew->proximity) goto out;
+
+ if (dsOrig->buttons != dsNew->buttons) goto out;
+ if (dsOrig->stripx != dsNew->stripx) goto out;
+ if (dsOrig->stripy != dsNew->stripy) goto out;
+
+ /* FIXME: we should have different suppress values for different
+ * axes with vastly different ranges.
+ */
+ if (abs(dsOrig->tiltx - dsNew->tiltx) > suppress) goto out;
+ if (abs(dsOrig->tilty - dsNew->tilty) > suppress) goto out;
+ if (abs(dsOrig->pressure - dsNew->pressure) > suppress) goto out;
+ if (abs(dsOrig->throttle - dsNew->throttle) > suppress) goto out;
+ if (abs(dsOrig->rotation - dsNew->rotation) > suppress &&
+ (1800 - abs(dsOrig->rotation - dsNew->rotation)) > suppress) goto out;
/* look for change in absolute wheel position
* or any relative wheel movement
*/
- if ((ABS(dsOrig->abswheel - dsNew->abswheel) > suppress)
- || (dsNew->relwheel != 0)) returnV = 1;
+ if (abs(dsOrig->abswheel - dsNew->abswheel) > suppress) goto out;
+ if (abs(dsOrig->abswheel2 - dsNew->abswheel2) > suppress) goto out;
+ if (dsNew->relwheel != 0) goto out;
- /* cursor moves or not? */
- if ((ABS(dsOrig->x - dsNew->x) > suppress) ||
- (ABS(dsOrig->y - dsNew->y) > suppress))
+ returnV = SUPPRESS_ALL;
+
+out:
+ /* Special handling for cursor: if nothing else changed but the
+ * pointer x/y, suppress all but cursor movement. This return value
+ * is used in commonDispatchDevice to short-cut event processing.
+ */
+ if ((abs(dsOrig->x - dsNew->x) > suppress) ||
+ (abs(dsOrig->y - dsNew->y) > suppress))
{
- if (!returnV) /* need to check if cursor moves or not */
- returnV = 2;
+ if (returnV == SUPPRESS_ALL)
+ returnV = SUPPRESS_NON_MOTION;
}
else /* don't move cursor */
{
@@ -984,19 +915,6 @@ static int wcmCheckSuppress(WacomCommonPtr common, const WacomDeviceState* dsOri
return returnV;
}
-/* reset raw data counters for filters */
-static void resetSampleCounter(const WacomChannelPtr pChannel)
-{
- /* if out of proximity, reset hardware filter */
- if (!pChannel->valid.state.proximity)
- {
- pChannel->nSamples = 0;
- pChannel->rawFilter.npoints = 0;
- pChannel->rawFilter.statex = 0;
- pChannel->rawFilter.statey = 0;
- }
-}
-
/*****************************************************************************
* wcmEvent -
* Handles suppression, transformation, filtering, and event dispatch.
@@ -1008,9 +926,8 @@ void wcmEvent(WacomCommonPtr common, unsigned int channel,
WacomDeviceState* pLast;
WacomDeviceState ds;
WacomChannelPtr pChannel;
- WacomFilterState* fs;
- int i, suppress = 0;
-
+ enum WacomSuppressMode suppress;
+ WacomDevicePtr priv = common->wcmDevices;
pChannel = common->wcmChannel + channel;
pLast = &pChannel->valid.state;
@@ -1024,91 +941,61 @@ void wcmEvent(WacomCommonPtr common, unsigned int channel,
* will need to change the values (ie. for error correction) */
ds = *pState;
- /* timestamp the state for velocity and acceleration analysis */
- ds.sample = (int)GetTimeInMillis();
DBG(10, common,
"c=%d i=%d t=%d s=%u x=%d y=%d b=%d "
- "p=%d rz=%d tx=%d ty=%d aw=%d rw=%d "
- "t=%d df=%d px=%d st=%d cs=%d \n",
+ "p=%d rz=%d tx=%d ty=%d aw=%d aw2=%d rw=%d "
+ "t=%d px=%d st=%d cs=%d \n",
channel,
ds.device_id,
ds.device_type,
ds.serial_num,
ds.x, ds.y, ds.buttons,
ds.pressure, ds.rotation, ds.tiltx,
- ds.tilty, ds.abswheel, ds.relwheel, ds.throttle,
- ds.discard_first, ds.proximity, ds.sample,
+ ds.tilty, ds.abswheel, ds.abswheel2, ds.relwheel, ds.throttle,
+ ds.proximity, ds.sample,
pChannel->nSamples);
- /* Discard the first 2 USB packages due to events delay */
- if ( (pChannel->nSamples < 2) && (common->wcmDevCls == &gWacomUSBDevice) &&
- ds.device_type != PAD_ID && (ds.device_type != TOUCH_ID) )
+ /* touch device is needed for gesture later */
+ if ((ds.device_type == TOUCH_ID) && !IsTouch(priv) &&
+ TabletHasFeature(common, WCM_2FGT))
{
- DBG(11, common,
- "discarded %dth USB data.\n",
- pChannel->nSamples);
- ++pChannel->nSamples;
- return; /* discard */
+
+ for (; priv != NULL && !IsTouch(priv); priv = priv->next);
+
+ if (priv == NULL || !IsTouch(priv))
+ {
+ priv = common->wcmDevices;
+ xf86Msg(X_ERROR, "could not find touch device "
+ "for device on %s.\n", common->device_path);
+ }
}
- if (strstr(common->wcmModel->name, "Intuos4"))
+ if (TabletHasFeature(common, WCM_ROTATION) &&
+ TabletHasFeature(common, WCM_RING) &&
+ ds.device_type == CURSOR_ID) /* I4 mouse */
{
/* convert Intuos4 mouse tilt to rotation */
- wcmTilt2R(&ds);
+ ds.rotation = wcmTilt2R(ds.tiltx, ds.tilty,
+ INTUOS4_CURSOR_ROTATION_OFFSET);
+ ds.tiltx = 0;
+ ds.tilty = 0;
}
- fs = &pChannel->rawFilter;
- if (!fs->npoints && ds.proximity)
+ /* Optionally filter values only while in proximity */
+ if (ds.proximity && ds.device_type != PAD_ID)
{
- DBG(11, common, "initialize Channel data.\n");
- /* store channel device state for later use */
- for (i=common->wcmRawSample - 1; i>=0; i--)
- {
- fs->x[i]= ds.x;
- fs->y[i]= ds.y;
- fs->tiltx[i] = ds.tiltx;
- fs->tilty[i] = ds.tilty;
- }
- ++fs->npoints;
- } else {
- /* Filter raw data, fix hardware defects, perform error correction */
- for (i=common->wcmRawSample - 1; i>0; i--)
- {
- fs->x[i]= fs->x[i-1];
- fs->y[i]= fs->y[i-1];
- }
- fs->x[0] = ds.x;
- fs->y[0] = ds.y;
- if (HANDLE_TILT(common) && (ds.device_type == STYLUS_ID || ds.device_type == ERASER_ID))
- {
- for (i=common->wcmRawSample - 1; i>0; i--)
- {
- fs->tiltx[i]= fs->tiltx[i-1];
- fs->tilty[i]= fs->tilty[i-1];
- }
- fs->tiltx[0] = ds.tiltx;
- fs->tilty[0] = ds.tilty;
- }
- if (RAW_FILTERING(common) && common->wcmModel->FilterRaw && ds.device_type != PAD_ID)
- {
- if (common->wcmModel->FilterRaw(common,pChannel,&ds))
- {
- DBG(10, common,
- "Raw filtering discarded data.\n");
- resetSampleCounter(pChannel);
- return; /* discard */
- }
- }
+ /* Start filter fresh when entering proximity */
+ if (!pLast->proximity)
+ wcmResetSampleCounter(pChannel);
- /* Discard unwanted data */
- suppress = wcmCheckSuppress(common, pLast, &ds);
- if (!suppress)
- {
- resetSampleCounter(pChannel);
- return;
- }
+ wcmFilterCoord(common,pChannel,&ds);
}
+ /* skip event if we don't have enough movement */
+ suppress = wcmCheckSuppress(common, pLast, &ds);
+ if (suppress == SUPPRESS_ALL)
+ return;
+
/* JEJ - Do not move this code without discussing it with me.
* The device state is invariant of any filtering performed below.
* Changing the device state after this point can and will cause
@@ -1122,134 +1009,30 @@ void wcmEvent(WacomCommonPtr common, unsigned int channel,
pChannel->valid.state = ds; /*save last raw sample */
if (pChannel->nSamples < common->wcmRawSample) ++pChannel->nSamples;
- /* process second finger data if exists
- * and both touch and geature are enabled */
- if ((ds.device_type == TOUCH_ID) &&
- common->wcmTouch && common->wcmGesture)
- {
- WacomChannelPtr pOtherChannel;
- WacomDeviceState dsOther;
-
- /* exit gesture mode when both fingers are out */
- if (channel)
- pOtherChannel = common->wcmChannel;
- else
- pOtherChannel = common->wcmChannel + 1;
- dsOther = pOtherChannel->valid.state;
-
- /* This is the only place to reset gesture mode
- * once a gesture mode is entered */
- if (!ds.proximity && !dsOther.proximity)
- {
- common->wcmGestureMode = 0;
-
- /* send a touch out-prox event here
- * in case the FF was out before the SF */
- channel = 0;
- }
- else
- {
- /* don't move the cursor if in gesture mode
- * wait for second finger data to process gestures */
- if (!channel && common->wcmGestureMode)
- goto ret;
-
- /* process gesture */
- if (channel)
- {
- wcmFingerTapToClick(common);
- goto ret;
- }
- }
- }
+ if ((ds.device_type == TOUCH_ID) && common->wcmTouch)
+ wcmGestureFilter(priv, channel);
- /* everything else falls here */
- commonDispatchDevice(common,channel,pChannel, suppress);
-ret:
- resetSampleCounter(pChannel);
+ /* For touch, only first finger moves the cursor */
+ if ((ds.device_type == TOUCH_ID && common->wcmTouch && !channel) ||
+ (ds.device_type != TOUCH_ID))
+ commonDispatchDevice(common,channel,pChannel, suppress);
}
-static int idtotype(int id)
-{
- int type = CURSOR_ID;
-
- /* tools with id, such as Intuos series and Cintiq 21UX */
- switch (id)
- {
- case 0x812: /* Inking pen */
- case 0x801: /* Intuos3 Inking pen */
- case 0x012:
- case 0x822: /* Pen */
- case 0x842:
- case 0x852:
- case 0x823: /* Intuos3 Grip Pen */
- case 0x813: /* Intuos3 Classic Pen */
- case 0x885: /* Intuos3 Marker Pen */
- case 0x022:
- case 0x832: /* Stroke pen */
- case 0x032:
- case 0xd12: /* Airbrush */
- case 0x912:
- case 0x112:
- case 0x913: /* Intuos3 Airbrush */
- type = STYLUS_ID;
- break;
- case 0x82a: /* Eraser */
- case 0x85a:
- case 0x91a:
- case 0xd1a:
- case 0x0fa:
- case 0x82b: /* Intuos3 Grip Pen Eraser */
- case 0x81b: /* Intuos3 Classic Pen Eraser */
- case 0x91b: /* Intuos3 Airbrush Eraser */
- type = ERASER_ID;
- break;
- }
- return type;
-}
-
-static void commonDispatchDevice(WacomCommonPtr common, unsigned int channel,
- const WacomChannelPtr pChannel, int suppress)
+/**
+ * Find the device the current events are meant for. If multiple tools are
+ * configured on this tablet, the one that matches the serial number for the
+ * current device state is returned. If none match, the tool that has a
+ * serial of 0 is returned.
+ *
+ * @param ds The current device state as read from the fd
+ * @return The tool that should be used to emit the current events.
+ */
+static WacomToolPtr findTool(const WacomCommonPtr common,
+ const WacomDeviceState *ds)
{
- InputInfoPtr pDev = NULL;
+ WacomToolPtr tooldefault = NULL;
WacomToolPtr tool = NULL;
- WacomToolPtr tooldef = NULL;
- WacomDeviceState* ds = &pChannel->valid.states[0];
- WacomDevicePtr priv = NULL;
-
- if (!ds->device_type && ds->proximity)
- {
- /* Tool may be on the tablet when X starts.
- * Figure out device type by device id
- */
- switch (ds->device_id)
- {
- case STYLUS_DEVICE_ID:
- ds->device_type = STYLUS_ID;
- break;
- case ERASER_DEVICE_ID:
- ds->device_type = ERASER_ID;
- break;
- case CURSOR_DEVICE_ID:
- ds->device_type = CURSOR_ID;
- break;
- case TOUCH_DEVICE_ID:
- ds->device_type = TOUCH_ID;
- break;
- default:
- ds->device_type = idtotype(ds->device_id);
- }
- if (ds->serial_num)
- for (tool = common->wcmTool; tool; tool = tool->next)
- if (ds->serial_num == tool->serial)
- {
- ds->device_type = tool->typeid;
- break;
- }
- }
- DBG(10, common, "device type = %d\n", ds->device_type);
- /* Find the device the current events are meant for */
/* 1: Find the tool (the one with correct serial or in second
* hand, the one with serial set to 0 if no match with the
* specified serial exists) that is used for this event */
@@ -1260,267 +1043,296 @@ static void commonDispatchDevice(WacomCommonPtr common, unsigned int channel,
if (tool->serial == ds->serial_num)
break;
else if (!tool->serial)
- tooldef = tool;
+ tooldefault = tool;
}
}
/* Use default tool (serial == 0) if no specific was found */
if (!tool)
- tool = tooldef;
+ tool = tooldefault;
+
+ return tool;
+}
+
+
+/**
+ * Return the minimum pressure based on the current minimum pressure and the
+ * hardware state. This is mainly to deal with the case where heavily used
+ * stylus may have a "pre-loaded" initial pressure. In that case, the tool
+ * comes into proximity with a pressure > 0 to begin with and thus offsets
+ * the pressure values. This preloaded pressure must be known for pressure
+ * normalisation to work.
+ *
+ * @param priv The wacom device
+ * @param ds Current device state
+ *
+ * @return The minimum pressure value for this tool.
+ *
+ * @see normalizePressure
+ */
+static int
+rebasePressure(const WacomDevicePtr priv, const WacomDeviceState *ds)
+{
+ int min_pressure;
+
+ /* set the minimum pressure when in prox */
+ if (!priv->oldProximity)
+ min_pressure = ds->pressure;
+ else
+ min_pressure = min(priv->minPressure, ds->pressure);
+
+ return min_pressure;
+}
+
+/**
+ * Instead of reporting the raw pressure, we normalize
+ * the pressure from 0 to FILTER_PRESSURE_RES. This is
+ * mainly to deal with the case where heavily used
+ * stylus may have a "pre-loaded" initial pressure. To
+ * do so, we keep the in-prox pressure and subtract it
+ * from the raw pressure to prevent a potential
+ * left-click before the pen touches the tablet.
+ *
+ * @param priv The wacom device
+ * @param ds Current device state
+ *
+ * @rebaes
+ * @see rebasePressure
+ */
+static int
+normalizePressure(const WacomDevicePtr priv, const WacomDeviceState *ds)
+{
+ WacomCommonPtr common = priv->common;
+ double pressure;
+ int p = ds->pressure;
- /* 2: Find the associated area, and its InputDevice */
- if (tool)
+ if (p < priv->minPressure)
{
- /* if the current area is not in-prox anymore, we
- * might want to use another area. So move the
- * current-pointer away for a moment while we have a
- * look if there's a better area defined.
- * Skip this if only one area is defined
- */
- WacomToolAreaPtr outprox = NULL;
- if (tool->current && tool->arealist->next &&
- !wcmPointInArea(tool->current, ds->x, ds->y))
- {
- outprox = tool->current;
- tool->current = NULL;
- }
+ xf86Msg(X_ERROR, "%s: Pressure %d lower than expected minimum %d. This is a bug.\n",
+ priv->pInfo->name, ds->pressure, priv->minPressure);
+ p = priv->minPressure;
+ }
- /* If only one area is defined for the tool, always
- * use this area even if we're not inside it
- */
- if (!tool->current && !tool->arealist->next)
- tool->current = tool->arealist;
+ /* normalize pressure to 0..FILTER_PRESSURE_RES */
+ pressure = xf86ScaleAxis(p - priv->minPressure,
+ FILTER_PRESSURE_RES, 0,
+ common->wcmMaxZ - priv->minPressure,
+ 0);
- /* If no current area in-prox, find a matching area */
- if(!tool->current)
- {
- WacomToolAreaPtr area = tool->arealist;
- for(; area; area = area->next)
- if (wcmPointInArea(area, ds->x, ds->y))
- break;
- tool->current = area;
- }
+ return (int)pressure;
+}
- /* If a better area was found, send a soft prox-out
- * for the current in-prox area, else use the old one. */
- if (outprox)
- {
- if (tool->current)
- {
- /* Send soft prox-out for the old area */
- InputInfoPtr oDev = outprox->device;
- WacomDeviceState out = { 0 };
- out.device_type = DEVICE_ID(((WacomDevicePtr)(oDev->private))->flags);
- DBG(2, common, "Soft prox-out for %s\n",
- outprox->device->name);
- wcmSendEvents(oDev, &out);
- }
- else
- tool->current = outprox;
- }
+/*
+ * Based on the current pressure, return the button state with Button1
+ * either set or unset, depending on whether the pressure threshold
+ * conditions have been met.
+ *
+ * Returns the state of all buttons, but buttons other than button 1 are
+ * unmodified.
+ */
+static int
+setPressureButton(const WacomDevicePtr priv, const WacomDeviceState *ds)
+{
+ WacomCommonPtr common = priv->common;
+ int button = 1;
+ int buttons = ds->buttons;
- /* If there was one already in use or we found one */
- if(tool->current)
+ /* button 1 Threshold test */
+ /* set button1 (left click) on/off */
+ if (ds->pressure < common->wcmThreshold)
+ {
+ buttons &= ~button;
+ if (priv->oldButtons & button) /* left click was on */
{
- pDev = tool->current->device;
- DBG(11, common, "tool id=%d for %s\n",
- ds->device_type, pDev->name);
+ /* don't set it off if it is within the tolerance
+ and threshold is larger than the tolerance */
+ if ((common->wcmThreshold > THRESHOLD_TOLERANCE) &&
+ (ds->pressure > common->wcmThreshold - THRESHOLD_TOLERANCE))
+ buttons |= button;
}
}
- /* X: InputDevice selection done! */
+ else
+ buttons |= button;
- /* Tool on the tablet when driver starts. This sometime causes
- * access errors to the device */
- if (pDev && !miPointerGetScreen(pDev->dev))
+ return buttons;
+}
+
+static void commonDispatchDevice(WacomCommonPtr common, unsigned int channel,
+ const WacomChannelPtr pChannel,
+ enum WacomSuppressMode suppress)
+{
+ InputInfoPtr pInfo = NULL;
+ WacomToolPtr tool = NULL;
+ WacomDeviceState* ds = &pChannel->valid.states[0];
+ WacomDevicePtr priv = NULL;
+ WacomDeviceState filtered;
+
+ /* device_type should have been retrieved and set in the respective
+ * models, wcmISDV4.c or wcmUSB.c. Once it comes here, something
+ * must have been wrong. Ignore the events.
+ */
+ if (!ds->device_type)
{
- xf86Msg(X_ERROR, "wcmEvent: Wacom driver can not get Current Screen ID\n");
- xf86Msg(X_ERROR, "Please remove Wacom tool from the tablet and bring it back again.\n");
+ DBG(11, common, "no device type matches with"
+ " serial=%u\n", ds->serial_num);
return;
}
+ DBG(10, common, "device type = %d\n", ds->device_type);
+
+ /* Find the device the current events are meant for */
+ tool = findTool(common, ds);
/* if a device matched criteria, handle filtering per device
* settings, and send event to XInput */
- if (pDev)
+ if (!tool || !tool->device)
{
- WacomDeviceState filtered = pChannel->valid.state;
+ DBG(11, common, "no device matches with"
+ " id=%d, serial=%u\n",
+ ds->device_type, ds->serial_num);
+ return;
+ }
- /* Device transformations come first */
- /* button 1 Threshold test */
- int button = 1;
- priv = pDev->private;
+ /* Tool on the tablet when driver starts. This sometime causes
+ * access errors to the device */
+ if (!tool->enabled) {
+ xf86Msg(X_ERROR, "tool not initialized yet. Skipping event. \n");
+ return;
+ }
- if (common->wcmDevCls == &gWacomUSBDevice && IsTouch(priv) && !ds->proximity)
- {
- priv->hardProx = 0;
- }
+ pInfo = tool->device;
+ DBG(11, common, "tool id=%d for %s\n", ds->device_type, pInfo->name);
- if (common->wcmDevCls == &gWacomUSBDevice && (IsStylus(priv) || IsEraser(priv)))
- {
- priv->hardProx = 1;
- }
-
- /* send a touch out for USB Tablet PCs */
- if (common->wcmDevCls == &gWacomUSBDevice && !IsTouch(priv)
- && common->wcmTouchDefault && !priv->oldProximity)
- {
- InputInfoPtr pInfoDevices = xf86FirstLocalDevice();
- WacomCommonPtr tempcommon = NULL;
- WacomDevicePtr temppriv = NULL;
-
- /* Lookup to see if associated touch was enabled */
- for (; pInfoDevices != NULL; pInfoDevices = pInfoDevices->next)
- {
- if (strstr(pInfoDevices->drv->driverName, "wacom"))
- {
- temppriv = (WacomDevicePtr) pInfoDevices->private;
- tempcommon = temppriv->common;
-
- if ((tempcommon->tablet_id == common->tablet_id) &&
- IsTouch(temppriv) && temppriv->oldProximity)
- {
- /* Send soft prox-out for touch first */
- WacomDeviceState out = { 0 };
- out.device_type = DEVICE_ID(temppriv->flags);
- DBG(2, common,
- "Send soft prox-out for %s first\n",
- pInfoDevices->name);
- wcmSendEvents(pInfoDevices, &out);
- }
- }
- }
- }
+ filtered = pChannel->valid.state;
+
+ /* Device transformations come first */
+ priv = pInfo->private;
- if (IsStylus(priv) || IsEraser(priv))
+ if (priv->serial && filtered.serial_num != priv->serial)
+ {
+ DBG(10, priv, "serial number"
+ " is %u but your system configured %u",
+ filtered.serial_num, priv->serial);
+ return;
+ }
+
+ if (TabletHasFeature(common, WCM_PENTOUCH))
+ {
+ if (IsPen(priv))
{
- /* set button1 (left click) on/off */
- if (filtered.pressure >= common->wcmThreshold)
- filtered.buttons |= button;
- else
+ /* send touch out when pen coming in-prox for devices
+ * that provideboth pen and touch events so system
+ * cursor won't jump between tools.
+ */
+ if (common->wcmTouchDevice->oldProximity)
{
- /* threshold tolerance */
- int tol = common->wcmMaxZ / 250;
- if (strstr(common->wcmModel->name, "Intuos4"))
- tol = common->wcmMaxZ / 125;
- if (filtered.pressure < common->wcmThreshold - tol)
- filtered.buttons &= ~button;
+ common->wcmGestureMode = 0;
+ wcmSoftOutEvent(common->wcmTouchDevice->pInfo);
+ return;
}
- /* transform pressure */
- transPressureCurve(priv,&filtered);
}
+ else if (IsTouch(priv) && common->wcmPenInProx)
+ /* Ignore touch events when pen is in prox */
+ return;
+ }
- /* touch capacity is supported */
- if (IsTouch(priv) && (common->wcmCapacityDefault >= 0) && !priv->hardProx)
- {
- if (((double)(filtered.capacity * 5) /
- (double)common->wcmMaxZ) >
- (5 - common->wcmCapacity))
- filtered.buttons |= button;
- }
- else if (IsCursor(priv) && !priv->hardProx)
- {
- /* initial current max distance */
- if (strstr(common->wcmModel->name, "Intuos"))
- common->wcmMaxCursorDist = 256;
- else
- common->wcmMaxCursorDist = 0;
- }
+ if (IsPen(priv))
+ common->wcmPenInProx = filtered.proximity;
+
+ if ((IsPen(priv) || IsTouch(priv)) && common->wcmMaxZ)
+ {
+ priv->minPressure = rebasePressure(priv, &filtered);
+ filtered.pressure = normalizePressure(priv, &filtered);
+ if (IsPen(priv))
+ filtered.buttons = setPressureButton(priv, &filtered);
+ filtered.pressure = applyPressureCurve(priv,&filtered);
+ }
+ else if (IsCursor(priv) && !priv->oldCursorHwProx)
+ {
+ /* initial current max distance for Intuos series */
+ if ((TabletHasFeature(common, WCM_ROTATION)) ||
+ (TabletHasFeature(common, WCM_DUALINPUT)))
+ common->wcmMaxCursorDist = common->wcmMaxDist;
+ else
+ common->wcmMaxCursorDist = 0;
+ }
- /* Store current hard prox for next use */
- if (!IsTouch(priv))
- priv->hardProx = ds->proximity;
+ /* Store cursor hardware prox for next use */
+ if (IsCursor(priv))
+ priv->oldCursorHwProx = ds->proximity;
- /* User-requested filtering comes next */
+ /* User-requested filtering comes next */
- /* User-requested transformations come last */
+ /* User-requested transformations come last */
+
+ if (!is_absolute(pInfo) && !IsPad(priv))
+ {
+ /* To improve the accuracy of relative x/y,
+ * don't send motion event when there is no movement.
+ */
+ double deltx = filtered.x - priv->oldX;
+ double delty = filtered.y - priv->oldY;
+ deltx *= priv->factorX;
+ delty *= priv->factorY;
- if ((!(priv->flags & ABSOLUTE_FLAG)) && (!IsPad(priv)))
+ /* less than one device coordinate movement? */
+ if (abs(deltx)<1 && abs(delty)<1)
{
- /* To improve the accuracy of relative x/y,
- * don't send motion event when there is no movement.
- */
- double deltx = filtered.x - priv->oldX;
- double delty = filtered.y - priv->oldY;
- deltx *= priv->factorX;
- delty *= priv->factorY;
-
- if (ABS(deltx)<1 && ABS(delty)<1)
+ /* We have no other data in this event, skip */
+ if (suppress == SUPPRESS_NON_MOTION)
{
- /* don't move the cursor */
- if (suppress == 1)
- {
- /* send other events, such as button/wheel */
- filtered.x = priv->oldX;
- filtered.y = priv->oldY;
- }
- else /* no other events to send */
- {
- DBG(10, common, "Ignore non-movement relative data \n");
- return;
- }
- }
- else
- {
- int temp = deltx;
- deltx = (double)temp/(priv->factorX);
- temp = delty;
- delty = (double)temp/(priv->factorY);
- filtered.x = deltx + priv->oldX;
- filtered.y = delty + priv->oldY;
+ DBG(10, common, "Ignore non-movement relative data \n");
+ return;
}
+
+ /* send other events, such as button/wheel */
+ filtered.x = priv->oldX;
+ filtered.y = priv->oldY;
}
+ }
- /* force out-prox when distance is outside wcmCursorProxoutDist for pucks */
- if (IsCursor(priv))
+ /* force out-prox when distance is outside wcmCursorProxoutDist for pucks */
+ if (IsCursor(priv))
+ {
+ if (common->wcmProtocolLevel == WCM_PROTOCOL_5)
{
- /* force out-prox when distance is outside wcmCursorProxoutDist. */
- if (common->wcmProtocolLevel == 5)
- {
- if (common->wcmMaxCursorDist > filtered.distance)
- common->wcmMaxCursorDist = filtered.distance;
- }
- else
- {
- if (common->wcmMaxCursorDist < filtered.distance)
- common->wcmMaxCursorDist = filtered.distance;
- }
- DBG(10, common, "Distance over"
+ /* protocol 5 distance starts from the MaxDist
+ * when getting in the prox.
+ */
+ if (common->wcmMaxCursorDist > filtered.distance)
+ common->wcmMaxCursorDist = filtered.distance;
+ }
+ else
+ {
+ /* protocol 4 distance is 0 when getting in the prox */
+ if (common->wcmMaxCursorDist < filtered.distance)
+ common->wcmMaxCursorDist = filtered.distance;
+ }
+ DBG(10, common, "Distance over"
" the tablet: %d, ProxoutDist: %d current"
" min/max %d hard prox: %d\n",
- filtered.distance,
- common->wcmCursorProxoutDist,
- common->wcmMaxCursorDist,
+ filtered.distance,
+ common->wcmCursorProxoutDist,
+ common->wcmMaxCursorDist,
ds->proximity);
- if (priv->oldProximity)
- {
- if (abs(filtered.distance - common->wcmMaxCursorDist)
- > common->wcmCursorProxoutDist)
- filtered.proximity = 0;
- }
- /* once it is out. Don't let it in until a hard in */
- /* or it gets inside wcmCursorProxoutDist */
- else
- {
- if (abs(filtered.distance - common->wcmMaxCursorDist) >
- common->wcmCursorProxoutDist && ds->proximity)
- return;
- if (!ds->proximity)
- return;
- }
+ if (priv->oldProximity)
+ {
+ if (abs(filtered.distance - common->wcmMaxCursorDist)
+ > common->wcmCursorProxoutDist)
+ filtered.proximity = 0;
+ }
+ /* once it is out. Don't let it in until a hard in */
+ /* or it gets inside wcmCursorProxoutDist */
+ else
+ {
+ if (abs(filtered.distance - common->wcmMaxCursorDist) >
+ common->wcmCursorProxoutDist && ds->proximity)
+ return;
+ if (!ds->proximity)
+ return;
}
- wcmSendEvents(pDev, &filtered);
- /* If out-prox, reset the current area pointer */
- if (!filtered.proximity)
- tool->current = NULL;
- }
-
- /* otherwise, if no device matched... */
- else
- {
- DBG(11, common, "no device matches with"
- " id=%d, serial=%u\n",
- ds->device_type, ds->serial_num);
}
+ wcmSendEvents(pInfo, &filtered);
}
/*****************************************************************************
@@ -1548,398 +1360,162 @@ int wcmInitTablet(InputInfoPtr pInfo, const char* id, float version)
if (common->wcmThreshold <= 0)
{
/* Threshold for counting pressure as a button */
- if (strstr(common->wcmModel->name, "Intuos4"))
- common->wcmThreshold = common->wcmMaxZ * 3 / 25;
- else
- common->wcmThreshold = common->wcmMaxZ * 3 / 50;
+ common->wcmThreshold = DEFAULT_THRESHOLD;
+
xf86Msg(X_PROBED, "%s: using pressure threshold of %d for button 1\n",
pInfo->name, common->wcmThreshold);
}
/* output tablet state as probed */
- xf86Msg(X_PROBED, "%s: Wacom %s tablet speed=%d maxX=%d maxY=%d maxZ=%d "
+ if (TabletHasFeature(common, WCM_PEN))
+ xf86Msg(X_PROBED, "%s: Wacom %s tablet maxX=%d maxY=%d maxZ=%d "
"resX=%d resY=%d tilt=%s\n",
pInfo->name,
- model->name, common->wcmISDV4Speed,
+ model->name,
common->wcmMaxX, common->wcmMaxY, common->wcmMaxZ,
common->wcmResolX, common->wcmResolY,
HANDLE_TILT(common) ? "enabled" : "disabled");
-
- /* start the tablet data */
- if (model->Start && (model->Start(pInfo) != Success))
- return !Success;
+ else
+ xf86Msg(X_PROBED, "%s: Wacom %s tablet maxX=%d maxY=%d maxZ=%d "
+ "resX=%d resY=%d \n",
+ pInfo->name,
+ model->name,
+ common->wcmMaxTouchX, common->wcmMaxTouchY,
+ common->wcmMaxZ,
+ common->wcmTouchResolX, common->wcmTouchResolY);
return Success;
}
-/*****************************************************************************
-** Transformations
-*****************************************************************************/
-
-static void transPressureCurve(WacomDevicePtr pDev, WacomDeviceStatePtr pState)
+/* Send a soft prox-out event for the device */
+void wcmSoftOutEvent(InputInfoPtr pInfo)
{
- if (pDev->pPressCurve)
- {
- int p = pState->pressure;
-
- /* clip */
- p = (p < 0) ? 0 : (p > pDev->common->wcmMaxZ) ?
- pDev->common->wcmMaxZ : p;
-
- /* rescale pressure to FILTER_PRESSURE_RES */
- p = (p * FILTER_PRESSURE_RES) / pDev->common->wcmMaxZ;
-
- /* apply pressure curve function */
- p = pDev->pPressCurve[p];
+ WacomDeviceState out = { 0 };
+ WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
- /* scale back to wcmMaxZ */
- pState->pressure = (p * pDev->common->wcmMaxZ) /
- FILTER_PRESSURE_RES;
- }
+ out.device_type = DEVICE_ID(priv->flags);
+ out.device_id = wcmGetPhyDeviceID(priv);
+ DBG(2, priv->common, "send a soft prox-out\n");
+ wcmSendEvents(pInfo, &out);
}
/*****************************************************************************
- * wcmInitialTVScreens
- ****************************************************************************/
+** Transformations
+*****************************************************************************/
-static void wcmInitialTVScreens(InputInfoPtr pInfo)
+/**
+ * Apply the current pressure curve to the current pressure.
+ *
+ * @return The modified pressure value.
+ */
+static int applyPressureCurve(WacomDevicePtr pDev, const WacomDeviceStatePtr pState)
{
- WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
-
- if (priv->twinview == TV_NONE)
- return;
-
- priv->numScreen = 2;
-
- if ((priv->twinview == TV_LEFT_RIGHT) || (priv->twinview == TV_RIGHT_LEFT))
- {
- /* it does not need the offset if always map to a specific screen */
- if (priv->screen_no == -1)
- {
- priv->tvoffsetX = 60;
- priv->tvoffsetY = 0;
- }
+ /* clip the pressure */
+ int p = max(0, pState->pressure);
- /* default resolution. this should never be hit since we
- * always set the tvresolution from wacomcpl */
- if(!priv->tvResolution[0])
- {
- priv->tvResolution[0] = screenInfo.screens[0]->width/2;
- priv->tvResolution[1] = screenInfo.screens[0]->height;
- priv->tvResolution[2] = priv->tvResolution[0];
- priv->tvResolution[3] = priv->tvResolution[1];
- }
- }
- else if ((priv->twinview == TV_ABOVE_BELOW) || (priv->twinview == TV_BELOW_ABOVE))
- {
- /* it does not need the offset if always map to a specific screen */
- if (priv->screen_no == -1)
- {
- priv->tvoffsetX = 0;
- priv->tvoffsetY = 60;
- }
-
- /* default resolution. this should never be hit since we
- * always set the tvresolution from wacomcpl */
- if(!priv->tvResolution[0])
- {
- priv->tvResolution[0] = screenInfo.screens[0]->width;
- priv->tvResolution[1] = screenInfo.screens[0]->height/2;
- priv->tvResolution[2] = priv->tvResolution[0];
- priv->tvResolution[3] = priv->tvResolution[1];
- }
- }
-
- /* initial screen info */
- if (priv->twinview == TV_ABOVE_BELOW)
- {
- priv->screenTopX[0] = 0;
- priv->screenTopY[0] = 0;
- priv->screenBottomX[0] = priv->tvResolution[0];
- priv->screenBottomY[0] = priv->tvResolution[1];
- priv->screenTopX[1] = 0;
- priv->screenTopY[1] = priv->tvResolution[1];
- priv->screenBottomX[1] = priv->tvResolution[2];
- priv->screenBottomY[1] = priv->tvResolution[1] + priv->tvResolution[3];
- }
- if (priv->twinview == TV_LEFT_RIGHT)
- {
- priv->screenTopX[0] = 0;
- priv->screenTopY[0] = 0;
- priv->screenBottomX[0] = priv->tvResolution[0];
- priv->screenBottomY[0] = priv->tvResolution[1];
- priv->screenTopX[1] = priv->tvResolution[0];
- priv->screenTopY[1] = 0;
- priv->screenBottomX[1] = priv->tvResolution[0] + priv->tvResolution[2];
- priv->screenBottomY[1] = priv->tvResolution[3];
- }
- if (priv->twinview == TV_BELOW_ABOVE)
- {
- priv->screenTopX[0] = 0;
- priv->screenTopY[0] = priv->tvResolution[1];
- priv->screenBottomX[0] = priv->tvResolution[2];
- priv->screenBottomY[0] = priv->tvResolution[1] + priv->tvResolution[3];
- priv->screenTopX[1] = 0;
- priv->screenTopY[1] = 0;
- priv->screenBottomX[1] = priv->tvResolution[0];
- priv->screenBottomY[1] = priv->tvResolution[1];
- }
- if (priv->twinview == TV_RIGHT_LEFT)
- {
- priv->screenTopX[0] = priv->tvResolution[0];
- priv->screenTopY[0] = 0;
- priv->screenBottomX[0] = priv->tvResolution[0] + priv->tvResolution[2];
- priv->screenBottomY[0] = priv->tvResolution[3];
- priv->screenTopX[1] = 0;
- priv->screenTopY[1] = 0;
- priv->screenBottomX[1] = priv->tvResolution[0];
- priv->screenBottomY[1] = priv->tvResolution[1];
- }
+ p = min(FILTER_PRESSURE_RES, p);
- DBG(10, priv,
- "topX0=%d topY0=%d bottomX0=%d bottomY0=%d "
- "topX1=%d topY1=%d bottomX1=%d bottomY1=%d \n",
- priv->screenTopX[0], priv->screenTopY[0],
- priv->screenBottomX[0], priv->screenBottomY[0],
- priv->screenTopX[1], priv->screenTopY[1],
- priv->screenBottomX[1], priv->screenBottomY[1]);
+ /* apply pressure curve function */
+ return pDev->pPressCurve[p];
}
/*****************************************************************************
- * wcmInitialScreens
+ * wcmRotateTablet
****************************************************************************/
-void wcmInitialScreens(InputInfoPtr pInfo)
+void wcmRotateTablet(InputInfoPtr pInfo, int value)
{
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
- int i;
-
- DBG(2, priv, "number of screen=%d \n", screenInfo.numScreens);
- priv->tvoffsetX = 0;
- priv->tvoffsetY = 0;
- if (priv->twinview != TV_NONE)
- {
- wcmInitialTVScreens(pInfo);
- return;
- }
-
- /* initial screen info */
- priv->numScreen = screenInfo.numScreens;
- priv->screenTopX[0] = 0;
- priv->screenTopY[0] = 0;
- priv->screenBottomX[0] = 0;
- priv->screenBottomY[0] = 0;
- for (i=0; i<screenInfo.numScreens; i++)
- {
- if (screenInfo.numScreens > 1)
- {
-/* dixScreenOrigins was removed from xserver without bumping the ABI.
- * 1.8.99.901 is the first release after the break. thanks. */
-#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1, 8, 99, 901, 0)
- priv->screenTopX[i] = dixScreenOrigins[i].x;
- priv->screenTopY[i] = dixScreenOrigins[i].y;
- priv->screenBottomX[i] = dixScreenOrigins[i].x;
- priv->screenBottomY[i] = dixScreenOrigins[i].y;
-#else
- priv->screenTopX[i] = screenInfo.screens[i]->x;
- priv->screenTopY[i] = screenInfo.screens[i]->y;
- priv->screenBottomX[i] = screenInfo.screens[i]->x;
- priv->screenBottomY[i] = screenInfo.screens[i]->y;
-
-#endif
-
- DBG(10, priv, "from dix: "
- "ScreenOrigins[%d].x=%d ScreenOrigins[%d].y=%d \n",
- i, priv->screenTopX[i], i, priv->screenTopY[i]);
- }
-
- priv->screenBottomX[i] += screenInfo.screens[i]->width;
- priv->screenBottomY[i] += screenInfo.screens[i]->height;
-
- DBG(10, priv,
- "topX[%d]=%d topY[%d]=%d bottomX[%d]=%d bottomY[%d]=%d \n",
- i, priv->screenTopX[i], i, priv->screenTopY[i],
- i, priv->screenBottomX[i], i, priv->screenBottomY[i]);
- }
-}
-
-/*****************************************************************************
- * rotateOneTool
- ****************************************************************************/
-
-static void rotateOneTool(WacomDevicePtr priv)
-{
WacomCommonPtr common = priv->common;
- WacomToolAreaPtr area = priv->toolarea;
- int tmpTopX, tmpTopY, tmpBottomX, tmpBottomY, oldMaxX, oldMaxY;
+ WacomToolPtr tool;
DBG(10, priv, "\n");
+ common->wcmRotate = value;
- oldMaxX = priv->maxX;
- oldMaxY = priv->maxY;
-
- tmpTopX = priv->topX;
- tmpBottomX = priv->bottomX;
- tmpTopY = priv->topY;
- tmpBottomY = priv->bottomY;
+ /* Only try updating properties once we're enabled, no point
+ * otherwise. */
+ tool = priv->tool;
+ if (tool->enabled)
+ wcmUpdateRotationProperty(priv);
+}
- if (common->wcmRotate == ROTATE_CW || common->wcmRotate == ROTATE_CCW)
- {
- priv->maxX = oldMaxY;
- priv->maxY = oldMaxX;
- }
+/* Common pointer refcounting utilities.
+ * Common is shared across all wacom devices off the same port. These
+ * functions implement basic refcounting to avoid double-frees and memleaks.
+ *
+ * Usage:
+ * wcmNewCommon() to create a new struct.
+ * wcmRefCommon() to get a new reference to an already exiting one.
+ * wcmFreeCommon() to unref. After the last ref has been unlinked, the
+ * struct is freed.
+ *
+ */
- switch (common->wcmRotate) {
- case ROTATE_CW:
- area->topX = priv->topX = tmpTopY;
- area->bottomX = priv->bottomX = tmpBottomY;
- area->topY = priv->topY = oldMaxX - tmpBottomX;
- area->bottomY = priv->bottomY =oldMaxX - tmpTopX;
- break;
- case ROTATE_CCW:
- area->topX = priv->topX = oldMaxY - tmpBottomY;
- area->bottomX = priv->bottomX = oldMaxY - tmpTopY;
- area->topY = priv->topY = tmpTopX;
- area->bottomY = priv->bottomY = tmpBottomX;
- break;
- case ROTATE_HALF:
- area->topX = priv->topX = oldMaxX - tmpBottomX;
- area->bottomX = priv->bottomX = oldMaxX - tmpTopX;
- area->topY = priv->topY= oldMaxY - tmpBottomY;
- area->bottomY = priv->bottomY = oldMaxY - tmpTopY;
- break;
- }
- wcmInitialCoordinates(priv->pInfo, 0);
- wcmInitialCoordinates(priv->pInfo, 1);
-
- if (tmpTopX != priv->topX)
- xf86ReplaceIntOption(priv->pInfo->options, "TopX", priv->topX);
- if (tmpTopY != priv->topY)
- xf86ReplaceIntOption(priv->pInfo->options, "TopY", priv->topY);
- if (tmpBottomX != priv->bottomX)
- xf86ReplaceIntOption(priv->pInfo->options, "BottomX", priv->bottomX);
- if (tmpBottomY != priv->bottomY)
- xf86ReplaceIntOption(priv->pInfo->options, "BottomY", priv->bottomY);
+WacomCommonPtr wcmNewCommon(void)
+{
+ WacomCommonPtr common;
+ common = calloc(1, sizeof(WacomCommonRec));
+ if (common)
+ common->refcnt = 1;
+
+ common->wcmFlags = 0; /* various flags */
+ common->wcmProtocolLevel = WCM_PROTOCOL_4; /* protocol level */
+ common->wcmTPCButton = 0; /* set Tablet PC button on/off */
+ common->wcmGestureParameters.wcmScrollDirection = 0;
+ common->wcmGestureParameters.wcmTapTime = 250;
+ common->wcmRotate = ROTATE_NONE; /* default tablet rotation to off */
+ common->wcmMaxX = 0; /* max digitizer logical X value */
+ common->wcmMaxY = 0; /* max digitizer logical Y value */
+ common->wcmMaxTouchX = 1024; /* max touch X value */
+ common->wcmMaxTouchY = 1024; /* max touch Y value */
+ common->wcmMaxStripX = 4096; /* Max fingerstrip X */
+ common->wcmMaxStripY = 4096; /* Max fingerstrip Y */
+ common->wcmMaxtiltX = 128; /* Max tilt in X directory */
+ common->wcmMaxtiltY = 128; /* Max tilt in Y directory */
+ common->wcmCursorProxoutDistDefault = PROXOUT_INTUOS_DISTANCE;
+ /* default to Intuos */
+ common->wcmSuppress = DEFAULT_SUPPRESS;
+ /* transmit position if increment is superior */
+ common->wcmRawSample = DEFAULT_SAMPLES;
+ /* number of raw data to be used to for filtering */
+
+ return common;
}
-/*****************************************************************************
- * wcmRotateTablet
- ****************************************************************************/
-void wcmRotateTablet(InputInfoPtr pInfo, int value)
+void wcmFreeCommon(WacomCommonPtr *ptr)
{
- WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
- WacomCommonPtr common = priv->common;
- WacomDevicePtr tmppriv;
- int oldRotation;
- int tmpTopX, tmpTopY, tmpBottomX, tmpBottomY, oldMaxX, oldMaxY;
-
- DBG(10, priv, "\n");
+ WacomCommonPtr common = *ptr;
- if (common->wcmRotate == value) /* initialization */
+ DBG(10, common, "common refcount dec to %d\n", common->refcnt - 1);
+ if (--common->refcnt == 0)
{
- rotateOneTool(priv);
- }
- else
- {
- oldRotation = common->wcmRotate;
- common->wcmRotate = value;
-
- /* rotate all devices at once! else they get misaligned */
- for (tmppriv = common->wcmDevices; tmppriv; tmppriv = tmppriv->next)
+ free(common->private);
+ while (common->serials)
{
- oldMaxX = tmppriv->maxX;
- oldMaxY = tmppriv->maxY;
-
- if (oldRotation == ROTATE_CW || oldRotation == ROTATE_CCW)
- {
- tmppriv->maxX = oldMaxY;
- tmppriv->maxY = oldMaxX;
- }
-
- tmpTopX = tmppriv->topX;
- tmpBottomX = tmppriv->bottomX;
- tmpTopY = tmppriv->topY;
- tmpBottomY = tmppriv->bottomY;
-
- /* recover to the unrotated xy-rectangles */
- switch (oldRotation) {
- case ROTATE_CW:
- tmppriv->topX = oldMaxY - tmpBottomY;
- tmppriv->bottomX = oldMaxY - tmpTopY;
- tmppriv->topY = tmpTopX;
- tmppriv->bottomY = tmpBottomX;
- break;
- case ROTATE_CCW:
- tmppriv->topX = tmpTopY;
- tmppriv->bottomX = tmpBottomY;
- tmppriv->topY = oldMaxX - tmpBottomX;
- tmppriv->bottomY = oldMaxX - tmpTopX;
- break;
- case ROTATE_HALF:
- tmppriv->topX = oldMaxX - tmpBottomX;
- tmppriv->bottomX = oldMaxX - tmpTopX;
- tmppriv->topY = oldMaxY - tmpBottomY;
- tmppriv->bottomY = oldMaxY - tmpTopY;
- break;
- }
+ WacomToolPtr next;
- /* and rotate them to the new value */
- rotateOneTool(tmppriv);
+ DBG(10, common, "Free common serial: %d %s\n",
+ common->serials->serial,
+ common->serials->name);
- switch(value) {
- case ROTATE_NONE:
- xf86ReplaceStrOption(pInfo->options, "Rotate", "NONE");
- break;
- case ROTATE_CW:
- xf86ReplaceStrOption(pInfo->options, "Rotate", "CW");
- break;
- case ROTATE_CCW:
- xf86ReplaceStrOption(pInfo->options, "Rotate", "CCW");
- break;
- case ROTATE_HALF:
- xf86ReplaceStrOption(pInfo->options, "Rotate", "HALF");
- break;
- }
+ next = common->serials->next;
+ free(common->serials);
+ common->serials = next;
}
+ free(common);
}
+ *ptr = NULL;
}
-/* wcmPointInArea - check whether the point is within the area */
-
-Bool wcmPointInArea(WacomToolAreaPtr area, int x, int y)
-{
- if (area->topX <= x && x <= area->bottomX &&
- area->topY <= y && y <= area->bottomY)
- return 1;
- return 0;
-}
-
-/* wcmAreasOverlap - check if two areas are overlapping */
-
-static Bool wcmAreasOverlap(WacomToolAreaPtr area1, WacomToolAreaPtr area2)
+WacomCommonPtr wcmRefCommon(WacomCommonPtr common)
{
- if (wcmPointInArea(area1, area2->topX, area2->topY) ||
- wcmPointInArea(area1, area2->topX, area2->bottomY) ||
- wcmPointInArea(area1, area2->bottomX, area2->topY) ||
- wcmPointInArea(area1, area2->bottomX, area2->bottomY))
- return 1;
- if (wcmPointInArea(area2, area1->topX, area1->topY) ||
- wcmPointInArea(area2, area1->topX, area1->bottomY) ||
- wcmPointInArea(area2, area1->bottomX, area1->topY) ||
- wcmPointInArea(area2, area1->bottomX, area1->bottomY))
- return 1;
- return 0;
-}
-
-/* wcmAreaListOverlap - check if the area overlaps any area in the list */
-Bool wcmAreaListOverlap(WacomToolAreaPtr area, WacomToolAreaPtr list)
-{
- for (; list; list=list->next)
- if (area != list && wcmAreasOverlap(list, area))
- return 1;
- return 0;
+ if (!common)
+ common = wcmNewCommon();
+ else
+ common->refcnt++;
+ DBG(10, common, "common refcount inc to %d\n", common->refcnt);
+ return common;
}
-
-/* vim: set noexpandtab shiftwidth=8: */
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/wcmConfig.c b/src/wcmConfig.c
index f07e0af..5920e11 100644
--- a/src/wcmConfig.c
+++ b/src/wcmConfig.c
@@ -26,6 +26,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
+#include <wacom-properties.h>
/*****************************************************************************
* wcmAllocate --
@@ -37,14 +38,13 @@ static int wcmAllocate(InputInfoPtr pInfo)
WacomDevicePtr priv = NULL;
WacomCommonPtr common = NULL;
WacomToolPtr tool = NULL;
- WacomToolAreaPtr area = NULL;
int i;
priv = calloc(1, sizeof(WacomDeviceRec));
if (!priv)
goto error;
- common = calloc(1, sizeof(WacomCommonRec));
+ common = wcmNewCommon();
if (!common)
goto error;
@@ -52,12 +52,6 @@ static int wcmAllocate(InputInfoPtr pInfo)
if(!tool)
goto error;
- area = calloc(1, sizeof(WacomToolArea));
- if (!area)
- goto error;
-
- pInfo->flags = 0;
- pInfo->fd = -1;
pInfo->device_control = gWacomModule.DevProc;
pInfo->read_input = gWacomModule.DevReadInput;
pInfo->control_proc = gWacomModule.DevChangeControl;
@@ -68,8 +62,7 @@ static int wcmAllocate(InputInfoPtr pInfo)
priv->next = NULL;
priv->pInfo = pInfo;
priv->common = common; /* common info pointer */
- priv->hardProx = 1; /* previous hardware proximity */
- priv->screen_no = -1; /* associated screen */
+ priv->oldCursorHwProx = 0; /* previous cursor hardware proximity */
priv->nPressCtrl [0] = 0; /* pressure curve x0 */
priv->nPressCtrl [1] = 0; /* pressure curve y0 */
priv->nPressCtrl [2] = 100; /* pressure curve x1 */
@@ -87,120 +80,95 @@ static int wcmAllocate(InputInfoPtr pInfo)
* later in wcmParseOptions, when we have IsPad() available */
priv->wheelup = 0; /* Default absolute wheel up event */
priv->wheeldn = 0; /* Default absolute wheel down event */
+ priv->wheel2up = 0; /* Default absolute wheel2 up event */
+ priv->wheel2dn = 0; /* Default absolute wheel2 down event */
priv->striplup = 4; /* Default left strip up event */
priv->stripldn = 5; /* Default left strip down event */
priv->striprup = 4; /* Default right strip up event */
priv->striprdn = 5; /* Default right strip down event */
priv->naxes = 6; /* Default number of axes */
- priv->numScreen = screenInfo.numScreens; /* configured screens count */
- priv->currentScreen = -1; /* current screen in display */
- priv->twinview = TV_NONE; /* not using twinview gfx */
- priv->wcmMMonitor = 1; /* enabled (=1) to support multi-monitor desktop. */
- /* disabled (=0) when user doesn't want to move the */
- /* cursor from one screen to another screen */
/* JEJ - throttle sampling code */
priv->throttleLimit = -1;
- common->wcmFlags = RAW_FILTERING_FLAG; /* various flags */
common->wcmDevices = priv;
- common->wcmProtocolLevel = 4; /* protocol level */
- common->wcmISDV4Speed = 38400; /* serial ISDV4 link speed */
-
- common->wcmDevCls = &gWacomUSBDevice; /* device-specific functions */
- common->wcmTPCButton =
- common->wcmTPCButtonDefault; /* set Tablet PC button on/off */
- common->wcmCapacity = -1; /* Capacity is disabled */
- common->wcmCapacityDefault = -1; /* default to -1 when capacity isn't supported */
- /* 3 when capacity is supported */
- common->wcmRotate = ROTATE_NONE; /* default tablet rotation to off */
- common->wcmMaxX = 0; /* max digitizer logical X value */
- common->wcmMaxY = 0; /* max digitizer logical Y value */
- common->wcmMaxTouchX = 1024; /* max touch X value */
- common->wcmMaxTouchY = 1024; /* max touch Y value */
- common->wcmMaxStripX = 4096; /* Max fingerstrip X */
- common->wcmMaxStripY = 4096; /* Max fingerstrip Y */
- common->wcmMaxtiltX = 128; /* Max tilt in X directory */
- common->wcmMaxtiltY = 128; /* Max tilt in Y directory */
- common->wcmCursorProxoutDistDefault = PROXOUT_INTUOS_DISTANCE;
- /* default to Intuos */
- common->wcmSuppress = DEFAULT_SUPPRESS;
- /* transmit position if increment is superior */
- common->wcmRawSample = DEFAULT_SAMPLES;
- /* number of raw data to be used to for filtering */
/* tool */
priv->tool = tool;
common->wcmTool = tool;
tool->next = NULL; /* next tool in list */
- tool->arealist = area; /* list of defined areas */
+ tool->device = pInfo;
/* tool->typeid is set once we know the type - see wcmSetType */
- /* tool area */
- priv->toolarea = area;
- area->next = NULL; /* next area in list */
- area->device = pInfo; /* associated WacomDevice */
+ /* timers */
+ priv->serial_timer = TimerSet(NULL, 0, 0, NULL, NULL);
return 1;
error:
- free(area);
free(tool);
- free(common);
+ wcmFreeCommon(&common);
free(priv);
return 0;
}
+/*****************************************************************************
+ * wcmFree --
+ * Free the memory allocated by wcmAllocate
+ ****************************************************************************/
+
+static void wcmFree(InputInfoPtr pInfo)
+{
+ WacomDevicePtr priv = pInfo->private;
+
+ if (!priv)
+ return;
+
+ TimerFree(priv->serial_timer);
+ free(priv->tool);
+ wcmFreeCommon(&priv->common);
+ free(priv);
+
+ pInfo->private = NULL;
+}
+
static int wcmSetType(InputInfoPtr pInfo, const char *type)
{
WacomDevicePtr priv = pInfo->private;
if (!type)
- {
- xf86Msg(X_ERROR, "%s: No type or invalid type specified.\n"
- "Must be one of stylus, touch, cursor, eraser, or pad\n",
- pInfo->name);
- return 0;
- }
+ goto invalid;
if (xf86NameCmp(type, "stylus") == 0)
{
priv->flags = ABSOLUTE_FLAG|STYLUS_ID;
- pInfo->type_name = XI_STYLUS;
+ pInfo->type_name = WACOM_PROP_XI_TYPE_STYLUS;
} else if (xf86NameCmp(type, "touch") == 0)
{
int flags = TOUCH_ID;
- if (priv->common->tablet_id < 0xd0 || priv->common->tablet_id > 0xd3)
+ if (TabletHasFeature(priv->common, WCM_LCD))
flags |= ABSOLUTE_FLAG;
priv->flags = flags;
- pInfo->type_name = XI_TOUCH;
+ pInfo->type_name = WACOM_PROP_XI_TYPE_TOUCH;
} else if (xf86NameCmp(type, "cursor") == 0)
{
priv->flags = CURSOR_ID;
- pInfo->type_name = XI_CURSOR;
+ pInfo->type_name = WACOM_PROP_XI_TYPE_CURSOR;
} else if (xf86NameCmp(type, "eraser") == 0)
{
priv->flags = ABSOLUTE_FLAG|ERASER_ID;
- pInfo->type_name = XI_ERASER;
+ pInfo->type_name = WACOM_PROP_XI_TYPE_ERASER;
} else if (xf86NameCmp(type, "pad") == 0)
{
- priv->flags = PAD_ID;
- pInfo->type_name = XI_PAD;
- }
+ priv->flags = ABSOLUTE_FLAG|PAD_ID;
+ pInfo->type_name = WACOM_PROP_XI_TYPE_PAD;
+ } else
+ goto invalid;
/* Set the device id of the "last seen" device on this tool */
- if (IsStylus(priv))
- priv->old_device_id = STYLUS_DEVICE_ID;
- else if (IsEraser(priv))
- priv->old_device_id = ERASER_DEVICE_ID;
- else if (IsCursor(priv))
- priv->old_device_id = CURSOR_DEVICE_ID;
- else if (IsTouch(priv))
- priv->old_device_id = TOUCH_DEVICE_ID;
- else
- priv->old_device_id = PAD_DEVICE_ID;
+ priv->old_device_id = wcmGetPhyDeviceID(priv);
if (!priv->tool)
return 0;
@@ -208,6 +176,26 @@ static int wcmSetType(InputInfoPtr pInfo, const char *type)
priv->tool->typeid = DEVICE_ID(priv->flags); /* tool type (stylus/touch/eraser/cursor/pad) */
return 1;
+
+invalid:
+ xf86Msg(X_ERROR, "%s: No type or invalid type specified.\n"
+ "Must be one of stylus, touch, cursor, eraser, or pad\n",
+ pInfo->name);
+ return 0;
+}
+
+int wcmGetPhyDeviceID(WacomDevicePtr priv)
+{
+ if (IsStylus(priv))
+ return STYLUS_DEVICE_ID;
+ else if (IsEraser(priv))
+ return ERASER_DEVICE_ID;
+ else if (IsCursor(priv))
+ return CURSOR_DEVICE_ID;
+ else if (IsTouch(priv))
+ return TOUCH_DEVICE_ID;
+ else
+ return PAD_DEVICE_ID;
}
/*
@@ -237,6 +225,10 @@ static void wcmUninit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
WacomDevicePtr dev;
WacomDevicePtr *prev;
+ WacomCommonPtr common = priv->common;
+
+ if (!priv)
+ goto out;
DBG(1, priv, "\n");
@@ -262,10 +254,29 @@ static void wcmUninit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
}
dev = next;
}
+
+ free(pInfo->name);
+ pInfo->name = NULL;
}
#endif
- prev = &priv->common->wcmDevices;
+ if (priv->tool)
+ {
+ WacomToolPtr *prev_tool = &common->wcmTool;
+ WacomToolPtr tool = *prev_tool;
+ while (tool)
+ {
+ if (tool == priv->tool)
+ {
+ *prev_tool = tool->next;
+ break;
+ }
+ prev_tool = &tool->next;
+ tool = tool->next;
+ }
+ }
+
+ prev = &common->wcmDevices;
dev = *prev;
while(dev)
{
@@ -278,14 +289,9 @@ static void wcmUninit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
dev = dev->next;
}
- /* free pressure curve */
- free(priv->pPressCurve);
-
- free(priv);
- pInfo->private = NULL;
-
-
- xf86DeleteInput(pInfo, 0);
+out:
+ wcmFree(pInfo);
+ xf86DeleteInput(pInfo, 0);
}
/* wcmMatchDevice - locate matching device and merge common structure. If an
@@ -293,14 +299,19 @@ static void wcmUninit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
* the new device's "common" struct and point to the one of the already
* existing one instead.
* Then add the new device to the now-shared common struct.
+ *
+ * Returns 1 on a found match or 0 otherwise.
+ * Common_return is set to the common struct in use by this device.
*/
-static Bool wcmMatchDevice(InputInfoPtr pLocal)
+static Bool wcmMatchDevice(InputInfoPtr pLocal, WacomCommonPtr *common_return)
{
WacomDevicePtr priv = (WacomDevicePtr)pLocal->private;
WacomCommonPtr common = priv->common;
InputInfoPtr pMatch = xf86FirstLocalDevice();
- if (!common->wcmDevice)
+ *common_return = common;
+
+ if (!common->device_path)
return 0;
for (; pMatch != NULL; pMatch = pMatch->next)
@@ -309,20 +320,127 @@ static Bool wcmMatchDevice(InputInfoPtr pLocal)
if ((pLocal != pMatch) &&
strstr(pMatch->drv->driverName, "wacom") &&
- !strcmp(privMatch->common->wcmDevice, common->wcmDevice))
+ !strcmp(privMatch->common->device_path, common->device_path))
{
- DBG(2, priv, "port share between"
- " %s and %s\n", pLocal->name, pMatch->name);
- free(common);
- common = priv->common = privMatch->common;
- priv->next = common->wcmDevices;
- common->wcmDevices = priv;
+ DBG(2, priv, "port share between %s and %s\n",
+ pLocal->name, pMatch->name);
+ /* FIXME: we loose the common->wcmTool here but it
+ * gets re-added during wcmParseOptions. This is
+ * currently required by the code, adding the tool
+ * again here means we trigger the duplicate tool
+ * detection */
+ wcmFreeCommon(&priv->common);
+ priv->common = wcmRefCommon(privMatch->common);
+ priv->next = priv->common->wcmDevices;
+ priv->common->wcmDevices = priv;
+ *common_return = priv->common;
return 1;
}
}
return 0;
}
+/**
+ * Detect the device's device class. We only support two classes right now,
+ * USB and ISDV4. Let each class try to detect the type by checking what's
+ * behind the fd.
+ */
+static Bool
+wcmDetectDeviceClass(const InputInfoPtr pInfo)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+
+ if (common->wcmDevCls)
+ return TRUE;
+
+ /* Bluetooth is also considered as USB */
+ if (gWacomISDV4Device.Detect(pInfo))
+ common->wcmDevCls = &gWacomISDV4Device;
+ else if (gWacomUSBDevice.Detect(pInfo))
+ common->wcmDevCls = &gWacomUSBDevice;
+ else
+ xf86Msg(X_ERROR, "%s: cannot identify device class.\n", pInfo->name);
+
+ return (common->wcmDevCls != NULL);
+}
+
+static Bool
+wcmInitModel(InputInfoPtr pInfo)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+ char id[BUFFER_SIZE];
+ float version;
+
+ /* Initialize the tablet */
+ if(common->wcmDevCls->Init(pInfo, id, &version) != Success ||
+ wcmInitTablet(pInfo, id, version) != Success)
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Link the touch tool to the pen of the same device
+ * so we can arbitrate the events when posting them.
+ */
+static void wcmLinkTouchAndPen(InputInfoPtr pInfo)
+{
+ WacomDevicePtr priv = pInfo->private;
+ WacomCommonPtr common = priv->common;
+ InputInfoPtr device = xf86FirstLocalDevice();
+ WacomCommonPtr tmpcommon = NULL;
+ WacomDevicePtr tmppriv = NULL;
+ Bool touch_device_assigned = FALSE;
+
+ /* Lookup to find the associated pen and touch */
+ for (; device != NULL; device = device->next)
+ {
+ if (!strcmp(device->drv->driverName, "wacom"))
+ {
+ tmppriv = (WacomDevicePtr) device->private;
+ tmpcommon = tmppriv->common;
+ touch_device_assigned = (common->wcmTouchDevice ||
+ tmpcommon->wcmTouchDevice);
+
+ /* skip the same tool or already linked devices */
+ if ((tmppriv == priv) || touch_device_assigned)
+ continue;
+
+ if (tmpcommon->tablet_id == common->tablet_id)
+ {
+ if (IsTouch(tmppriv) && IsPen(priv))
+ common->wcmTouchDevice = tmppriv;
+ else if (IsTouch(priv) && IsPen(tmppriv))
+ tmpcommon->wcmTouchDevice = priv;
+
+ if (common->wcmTouchDevice ||
+ tmpcommon->wcmTouchDevice)
+ {
+ TabletSetFeature(common, WCM_PENTOUCH);
+ TabletSetFeature(tmpcommon, WCM_PENTOUCH);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Check if this device was hotplugged by the driver by checking the _source
+ * option.
+ *
+ * Must be called before wcmNeedAutoHotplug()
+ *
+ * @return True if the source for this device is the wacom driver itself or
+ * false otherwise.
+ */
+static int wcmIsHotpluggedDevice(InputInfoPtr pInfo)
+{
+ char *source = xf86CheckStrOption(pInfo->options, "_source", "");
+ return !strcmp(source, "_driver/wacom");
+}
+
/* wcmPreInit - called for each input devices with the driver set to
* "wacom" */
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12
@@ -342,6 +460,7 @@ static InputInfoPtr wcmPreInit(InputDriverPtr drv, IDevPtr dev, int flags)
* phase is based on those values.
*/
xf86CollectInputOptions(pInfo, (const char**)default_options, NULL);
+ xf86ProcessCommonOptions(pInfo, pInfo->options);
if (NewWcmPreInit(drv, pInfo, flags) == Success) {
pInfo->flags |= XI86_CONFIGURED;
@@ -360,13 +479,13 @@ static int wcmPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
WacomDevicePtr priv = NULL;
WacomCommonPtr common = NULL;
const char* type;
- char* device;
- int need_hotplug = 0;
+ char* device, *oldname;
+ int need_hotplug = 0, is_dependent = 0;
gWacomModule.wcmDrv = drv;
device = xf86SetStrOption(pInfo->options, "Device", NULL);
- type = xf86FindOptionValue(pInfo->options, "Type");
+ type = xf86SetStrOption(pInfo->options, "Type", NULL);
/*
Init process:
@@ -383,54 +502,67 @@ static int wcmPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
if (!wcmAllocate(pInfo))
goto SetupProc_fail;
- if (!device)
- {
- if (!wcmAutoProbeDevice(pInfo))
- goto SetupProc_fail;
-
- device = xf86SetStrOption(pInfo->options, "Device", NULL);
- }
-
- SYSCALL(pInfo->fd = open(device, O_RDONLY));
- if (pInfo->fd < 0)
- {
- xf86Msg(X_WARNING, "%s: failed to open %s.\n",
- pInfo->name, device);
+ if (!device && !(device = wcmEventAutoDevProbe(pInfo)))
goto SetupProc_fail;
- }
priv = (WacomDevicePtr) pInfo->private;
- common = priv->common;
+ priv->common->device_path = device;
priv->name = pInfo->name;
- common->wcmDevice = device;
+ priv->debugLevel = xf86SetIntOption(pInfo->options,
+ "DebugLevel", priv->debugLevel);
+
+ /* check if the same device file has been added already */
+ if (wcmIsDuplicate(device, pInfo))
+ goto SetupProc_fail;
+
+ if (wcmOpen(pInfo) != Success)
+ goto SetupProc_fail;
+
+ /* Try to guess whether it's USB or ISDV4 */
+ if (!wcmDetectDeviceClass(pInfo))
+ goto SetupProc_fail;
/* check if this is the first tool on the port */
- if (!wcmMatchDevice(pInfo))
+ if (!wcmMatchDevice(pInfo, &common))
/* initialize supported keys with the first tool on the port */
wcmDeviceTypeKeys(pInfo);
- need_hotplug = wcmNeedAutoHotplug(pInfo, &type);
+ common->debugLevel = xf86SetIntOption(pInfo->options,
+ "CommonDBG", common->debugLevel);
+ oldname = pInfo->name;
+
+ if (wcmIsHotpluggedDevice(pInfo))
+ is_dependent = 1;
+ else if ((need_hotplug = wcmNeedAutoHotplug(pInfo, &type)))
+ {
+ /* we need subdevices, change the name so all of them have a
+ type. */
+ char *new_name;
+ if (asprintf(&new_name, "%s %s", pInfo->name, type) == -1)
+ new_name = strdup(pInfo->name);
+ pInfo->name = priv->name = new_name;
+ }
/* check if the type is valid for those don't need hotplug */
if(!need_hotplug && !wcmIsAValidType(pInfo, type))
goto SetupProc_fail;
- /* check if the same device file has been added already */
- if (wcmIsDuplicate(device, pInfo))
+ if (!wcmSetType(pInfo, type))
goto SetupProc_fail;
- if (!wcmSetType(pInfo, type))
+ if (!wcmPreInitParseOptions(pInfo, need_hotplug, is_dependent))
goto SetupProc_fail;
- /* Process the common options. */
- xf86ProcessCommonOptions(pInfo, pInfo->options);
- if (!wcmParseOptions(pInfo))
+ if (!wcmInitModel(pInfo))
+ goto SetupProc_fail;
+
+ if (!wcmPostInitParseOptions(pInfo, need_hotplug, is_dependent))
goto SetupProc_fail;
if (need_hotplug)
{
priv->isParent = 1;
- wcmHotplugOthers(pInfo);
+ wcmHotplugOthers(pInfo, oldname);
}
if (pInfo->fd != -1)
@@ -439,6 +571,12 @@ static int wcmPreInit(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
pInfo->fd = -1;
}
+ /* only link them once per port. We need to try for both pen and touch
+ * since we do not know which tool (touch or pen) will be added first.
+ */
+ if (IsTouch(priv) || (IsPen(priv) && !common->wcmTouchDevice))
+ wcmLinkTouchAndPen(pInfo);
+
return Success;
SetupProc_fail:
@@ -446,7 +584,7 @@ SetupProc_fail:
if (common && priv)
common->wcmDevices = priv->next;
- if (pInfo && pInfo->fd != -1)
+ if (pInfo->fd != -1)
{
close(pInfo->fd);
pInfo->fd = -1;
@@ -505,4 +643,4 @@ _X_EXPORT XF86ModuleData wacomModuleData =
wcmUnplug
};
-/* vim: set noexpandtab shiftwidth=8: */
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/wcmFilter.c b/src/wcmFilter.c
index bf578cb..47e958a 100644
--- a/src/wcmFilter.c
+++ b/src/wcmFilter.c
@@ -35,8 +35,6 @@ static void filterCurveToLine(int* pCurve, int nMax, double x0, double y0,
static int filterOnLine(double x0, double y0, double x1, double y1,
double a, double b);
static void filterLine(int* pCurve, int nMax, int x0, int y0, int x1, int y1);
-static void filterIntuosStylus(WacomCommonPtr common, WacomFilterStatePtr state, WacomDeviceStatePtr ds);
-void wcmTilt2R(WacomDeviceStatePtr ds);
/*****************************************************************************
@@ -62,19 +60,6 @@ void wcmSetPressureCurve(WacomDevicePtr pDev, int x0, int y0,
if (!wcmCheckPressureCurveValues(x0, y0, x1, y1))
return;
- /* if curve is not allocated, do it now. */
- if (!pDev->pPressCurve)
- {
- pDev->pPressCurve = (int*) malloc(sizeof(int) *
- (FILTER_PRESSURE_RES + 1));
- if (!pDev->pPressCurve)
- {
- xf86Msg(X_ERROR, "%s: wcmSetPressureCurve: failed to "
- "allocate memory for curve\n", pDev->pInfo->name);
- return;
- }
- }
-
/* linear by default */
for (i=0; i<=FILTER_PRESSURE_RES; ++i)
pDev->pPressCurve[i] = i;
@@ -93,6 +78,19 @@ void wcmSetPressureCurve(WacomDevicePtr pDev, int x0, int y0,
pDev->nPressCtrl[3] = y1;
}
+/*
+ * wcmResetSampleCounter --
+ * Device specific filter routines are responcable for storing raw data
+ * as well as filtering. wcmResetSampleCounter is called to reset
+ * raw counters.
+ */
+void wcmResetSampleCounter(const WacomChannelPtr pChannel)
+{
+ pChannel->nSamples = 0;
+ pChannel->rawFilter.npoints = 0;
+}
+
+
static void filterNearestPoint(double x0, double y0, double x1, double y1,
double a, double b, double* x, double* y)
{
@@ -215,38 +213,55 @@ static void filterLine(int* pCurve, int nMax, int x0, int y0, int x1, int y1)
}
}
}
-
-/*****************************************************************************
- * filterIntuosStylus --
- * Correct some hardware defects we've been seeing in Intuos pads,
- * but also cuts down quite a bit on jitter.
- ****************************************************************************/
-
-static void filterIntuosStylus(WacomCommonPtr common, WacomFilterStatePtr state, WacomDeviceStatePtr ds)
+static void storeRawSample(WacomCommonPtr common, WacomChannelPtr pChannel,
+ WacomDeviceStatePtr ds)
{
- int x=0, y=0, tx=0, ty=0, i;
+ WacomFilterState *fs;
+ int i;
- for ( i=0; i<common->wcmRawSample; i++ )
+ fs = &pChannel->rawFilter;
+ if (!fs->npoints)
{
- x += state->x[i];
- y += state->y[i];
- tx += state->tiltx[i];
- ty += state->tilty[i];
+ DBG(10, common, "initialize channel data.\n");
+ /* Store initial value over whole average window */
+ for (i=common->wcmRawSample - 1; i>=0; i--)
+ {
+ fs->x[i]= ds->x;
+ fs->y[i]= ds->y;
+ }
+ if (HANDLE_TILT(common) && (ds->device_type == STYLUS_ID ||
+ ds->device_type == ERASER_ID))
+ {
+ for (i=common->wcmRawSample - 1; i>=0; i--)
+ {
+ fs->tiltx[i] = ds->tiltx;
+ fs->tilty[i] = ds->tilty;
+ }
+ }
+ ++fs->npoints;
+ } else {
+ /* Shift window and insert latest sample */
+ for (i=common->wcmRawSample - 1; i>0; i--)
+ {
+ fs->x[i]= fs->x[i-1];
+ fs->y[i]= fs->y[i-1];
+ }
+ fs->x[0] = ds->x;
+ fs->y[0] = ds->y;
+ if (HANDLE_TILT(common) && (ds->device_type == STYLUS_ID ||
+ ds->device_type == ERASER_ID))
+ {
+ for (i=common->wcmRawSample - 1; i>0; i--)
+ {
+ fs->tiltx[i]= fs->tiltx[i-1];
+ fs->tilty[i]= fs->tilty[i-1];
+ }
+ fs->tiltx[0] = ds->tiltx;
+ fs->tilty[0] = ds->tilty;
+ }
+ if (fs->npoints < common->wcmRawSample)
+ ++fs->npoints;
}
- ds->x = x / common->wcmRawSample;
- ds->y = y / common->wcmRawSample;
-
- ds->tiltx = tx / common->wcmRawSample;
- if (ds->tiltx > common->wcmMaxtiltX/2-1)
- ds->tiltx = common->wcmMaxtiltX/2-1;
- else if (ds->tiltx < -common->wcmMaxtiltX/2)
- ds->tiltx = -common->wcmMaxtiltX/2;
-
- ds->tilty = ty / common->wcmRawSample;
- if (ds->tilty > common->wcmMaxtiltY/2-1)
- ds->tilty = common->wcmMaxtiltY/2-1;
- else if (ds->tilty < -common->wcmMaxtiltY/2)
- ds->tilty = -common->wcmMaxtiltY/2;
}
/*****************************************************************************
@@ -256,73 +271,106 @@ static void filterIntuosStylus(WacomCommonPtr common, WacomFilterStatePtr state,
int wcmFilterCoord(WacomCommonPtr common, WacomChannelPtr pChannel,
WacomDeviceStatePtr ds)
{
- /* Only noise correction should happen here. If there's a problem that
- * cannot be fixed, return 1 such that the data is discarded. */
-
- WacomDeviceState *pLast;
- int *x, *y, i;
+ int x=0, y=0, tx=0, ty=0, i;
+ WacomFilterState *state;
DBG(10, common, "common->wcmRawSample = %d \n", common->wcmRawSample);
- x = pChannel->rawFilter.x;
- y = pChannel->rawFilter.y;
- pLast = &pChannel->valid.state;
- ds->x = 0;
- ds->y = 0;
+ storeRawSample(common, pChannel, ds);
+
+ state = &pChannel->rawFilter;
for ( i=0; i<common->wcmRawSample; i++ )
{
- ds->x += x[i];
- ds->y += y[i];
+ x += state->x[i];
+ y += state->y[i];
+ if (HANDLE_TILT(common) && (ds->device_type == STYLUS_ID ||
+ ds->device_type == ERASER_ID))
+ {
+ tx += state->tiltx[i];
+ ty += state->tilty[i];
+ }
}
- ds->x /= common->wcmRawSample;
- ds->y /= common->wcmRawSample;
-
- return 0; /* lookin' good */
-}
-
-/*****************************************************************************
- * wcmFilterIntuos -- provide error correction to Intuos and Intuos2
- ****************************************************************************/
-
-int wcmFilterIntuos(WacomCommonPtr common, WacomChannelPtr pChannel,
- WacomDeviceStatePtr ds)
-{
- /* Only error correction should happen here. If there's a problem that
- * cannot be fixed, return 1 such that the data is discarded. */
+ ds->x = x / common->wcmRawSample;
+ ds->y = y / common->wcmRawSample;
- if (ds->device_type != CURSOR_ID)
- filterIntuosStylus(common, &pChannel->rawFilter, ds);
- else
- wcmFilterCoord(common, pChannel, ds);
+ if (HANDLE_TILT(common) && (ds->device_type == STYLUS_ID ||
+ ds->device_type == ERASER_ID))
+ {
+ ds->tiltx = tx / common->wcmRawSample;
+ if (ds->tiltx > common->wcmMaxtiltX/2-1)
+ ds->tiltx = common->wcmMaxtiltX/2-1;
+ else if (ds->tiltx < -common->wcmMaxtiltX/2)
+ ds->tiltx = -common->wcmMaxtiltX/2;
+
+ ds->tilty = ty / common->wcmRawSample;
+ if (ds->tilty > common->wcmMaxtiltY/2-1)
+ ds->tilty = common->wcmMaxtiltY/2-1;
+ else if (ds->tilty < -common->wcmMaxtiltY/2)
+ ds->tilty = -common->wcmMaxtiltY/2;
+ }
return 0; /* lookin' good */
}
-/*****************************************************************************
- * wcmTilt2R -
- * Converts tilt X and Y to rotation, for Intuos4 mouse for now.
- * It can be used for other devices when necessary.
- ****************************************************************************/
+/***
+ * Convert a point (X/Y) in a left-handed coordinate system to a normalized
+ * rotation angle.
+ *
+ * This function is currently called for the Intuos4 mouse (cursor) tool
+ * only (to convert tilt to rotation), but it may be used for other devices
+ * in the future.
+ *
+ * Method used: rotation angle is calculated through the atan of x/y
+ * then converted to degrees and normalized into the rotation
+ * range (MIN_ROTATION/MAX_ROTATION).
-void wcmTilt2R(WacomDeviceStatePtr ds)
+ * IMPORTANT: calculation inverts direction, the formula to get the target
+ * rotation value in degrees is: 360 - offset - input-angle.
+ *
+ * Example table of return values for an offset of 0, assuming a left-handed
+ * coordinate system:
+ * input 0 degrees: MIN
+ * 90 degrees: MAX - RANGE/4
+ * 180 degrees: RANGE/2
+ * 270 degrees: MIN + RANGE/4
+ *
+ * @param x X coordinate in left-handed coordiante system.
+ * @param y Y coordiante in left-handed coordinate system.
+ * @param offset Custom rotation offset in degrees. Offset is
+ * applied in counterclockwise direction.
+ *
+ * @return The mapped rotation angle based on the device's tilt state.
+ */
+int wcmTilt2R(int x, int y, double offset)
{
- short tilt_x = ds->tiltx;
- short tilt_y = ds->tilty;
- double rotation = 0.0;
-
- /* other tilt-enabled devices need to apply round() after this call */
- if (tilt_x || tilt_y)
- rotation = ((180.0 * atan2(-tilt_x,tilt_y)) / M_PI) + 180.0;
-
- /* Intuos4 mouse has an (180-5) offset */
- ds->rotation = round((360.0 - rotation + 180.0 - 5.0) * 5.0);
- ds->rotation %= 1800;
-
- if (ds->rotation >= 900)
- ds->rotation = 1800 - ds->rotation;
- else
- ds->rotation = -ds->rotation;
+ double angle = 0.0;
+ int rotation;
+
+ if (x || y)
+ /* rotate in the inverse direction, changing CW to CCW
+ * rotation and vice versa */
+ angle = ((180.0 * atan2(-x, y)) / M_PI);
+
+ /* rotation is now in 0 - 360 deg value range, apply the offset. Use
+ * 360 to avoid getting into negative range, the normalization code
+ * below expects 0...360 */
+ angle = 360 + angle - offset;
+
+ /* normalize into the rotation range (0...MAX), then offset by MIN_ROTATION
+ we used 360 as base offset above, so %= MAX_ROTATION_RANGE brings us back.
+ Note: we can't use xf86ScaleAxis here because of rounding issues.
+ */
+ rotation = round(angle * (MAX_ROTATION_RANGE / 360.0));
+ rotation %= MAX_ROTATION_RANGE;
+
+ /* now scale back from 0...MAX to MIN..(MIN+MAX) */
+ rotation = xf86ScaleAxis(rotation,
+ MIN_ROTATION + MAX_ROTATION_RANGE,
+ MIN_ROTATION,
+ MAX_ROTATION_RANGE, 0);
+
+ return rotation;
}
-/* vim: set noexpandtab shiftwidth=8: */
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/wcmFilter.h b/src/wcmFilter.h
index db84dc9..be86eb5 100644
--- a/src/wcmFilter.h
+++ b/src/wcmFilter.h
@@ -26,10 +26,9 @@
void wcmSetPressureCurve(WacomDevicePtr pDev, int x0, int y0,
int x1, int y1);
-int wcmFilterIntuos(WacomCommonPtr common, WacomChannelPtr pChannel,
- WacomDeviceStatePtr ds);
int wcmFilterCoord(WacomCommonPtr common, WacomChannelPtr pChannel,
WacomDeviceStatePtr ds);
+void wcmResetSampleCounter(const WacomChannelPtr pChannel);
/****************************************************************************/
#endif /* __XF86_WCMFILTER_H */
diff --git a/src/wcmISDV4.c b/src/wcmISDV4.c
index 004ee8b..5a123cd 100644
--- a/src/wcmISDV4.c
+++ b/src/wcmISDV4.c
@@ -1,6 +1,6 @@
/*
* Copyright 1995-2002 by Frederic Lepied, France. <Lepied@XFree86.org>
- * Copyright 2002-2010 by Ping Cheng, Wacom. <pingc@wacom.com>
+ * Copyright 2002-2011 by Ping Cheng, Wacom. <pingc@wacom.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -25,17 +25,53 @@
#include <xf86_OSproc.h>
#include "wcmFilter.h"
#include <linux/serial.h>
-#include "wcmISDV4.h"
-
-#define ISDV4_QUERY "*" /* ISDV4 query command */
-#define ISDV4_TOUCH_QUERY "%" /* ISDV4 touch query command */
-#define ISDV4_STOP "0" /* ISDV4 stop command */
-#define ISDV4_SAMPLING "1" /* ISDV4 sampling command */
+#include "isdv4.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <libudev.h>
#define RESET_RELATIVE(ds) do { (ds).relwheel = 0; } while (0)
+/* resolution in points/m */
+#define ISDV4_PEN_RESOLUTION 100000
+#define ISDV4_TOUCH_RESOLUTION 10000
+
+/* ISDV4 init process
+ This process is the same for other backends (i.e. USB).
+
+ 1. isdv4Detect - called to test if device may be serial
+ 2. isdv4ProbeKeys - called to fake up keybits
+ 3. isdv4ParseOptions - parse ISDV4-specific options
+ 4. isdv4Init - init ISDV4-specific stuff and set the tablet model.
+
+ After isdv4Init has been called, common->model points to the ISDV4 model,
+ further calls are model-specific (not that it matters for ISDV4, we only
+ have one model).
+
+ 5. isdv4InitISDV4 - do whatever device-specific init is necessary
+ 6. isdv4GetRanges - Query axis ranges
+
+ --- end of PreInit ---
+
+ isdv4StartTablet is called in DEVICE_ON
+ isdv4Parse is called during ReadInput.
+
+ */
+
+typedef struct {
+ /* Counter for dependent devices. We can only send one QUERY command to
+ the tablet and we must not send the SAMPLING command until the last
+ device is enabled. */
+ int initialized_devices;
+ /* QUERY can only be run once */
+ int tablet_initialized;
+ int baudrate;
+} wcmISDV4Data;
+
static Bool isdv4Detect(InputInfoPtr);
+static Bool isdv4ParseOptions(InputInfoPtr pInfo);
static Bool isdv4Init(InputInfoPtr, char* id, float *version);
+static int isdv4ProbeKeys(InputInfoPtr pInfo);
static void isdv4InitISDV4(WacomCommonPtr, const char* id, float version);
static int isdv4GetRanges(InputInfoPtr);
static int isdv4StartTablet(InputInfoPtr);
@@ -45,21 +81,12 @@ static int wcmSerialValidate(InputInfoPtr pInfo, const unsigned char* data);
static int wcmWaitForTablet(InputInfoPtr pInfo, char * data, int size);
static int wcmWriteWait(InputInfoPtr pInfo, const char* request);
-static inline int isdv4ParseQuery(const char *buffer, const size_t len,
- ISDV4QueryReply *reply);
-static inline int isdv4ParseTouchQuery(const char *buffer, const size_t len,
- ISDV4TouchQueryReply *reply);
-
-static inline int isdv4ParseTouchData(const unsigned char *buffer, const size_t len,
- const size_t pktlen, ISDV4TouchData *touchdata);
-
-static inline int isdv4ParseCoordinateData(const unsigned char *buffer, const size_t len,
- ISDV4CoordinateData *coord);
-
WacomDeviceClass gWacomISDV4Device =
{
isdv4Detect,
+ isdv4ParseOptions,
isdv4Init,
+ isdv4ProbeKeys,
};
static WacomModel isdv4General =
@@ -72,6 +99,27 @@ static inline int isdv4ParseCoordinateData(const unsigned char *buffer, const si
isdv4Parse,
};
+static void memdump(InputInfoPtr pInfo, char *buffer, unsigned int len)
+{
+#ifdef DEBUG
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+ int i;
+
+ DBG(10, common, "memdump of ISDV4 data (len %d)\n", len);
+ /* can't use DBG macro here, need to do it manually. */
+ for (i = 0 ; i < len && common->debugLevel >= 10; i++)
+ {
+ xf86Msg(X_NONE, "%#hhx ", buffer[i]);
+ if (i % 8 == 7)
+ xf86Msg(X_NONE, "\n");
+ }
+
+ xf86Msg(X_NONE, "\n");
+#endif
+}
+
+
static int wcmWait(int t)
{
int err = xf86WaitForInput(-1, ((t) * 1000));
@@ -144,9 +192,60 @@ static int wcmSerialValidate(InputInfoPtr pInfo, const unsigned char* data)
static Bool isdv4Detect(InputInfoPtr pInfo)
{
- WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+ struct serial_struct ser;
+ int rc;
+
+ rc = ioctl(pInfo->fd, TIOCGSERIAL, &ser);
+ if (rc == -1)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*****************************************************************************
+ * isdv4ParseOptions -- parse ISDV4-specific options
+ ****************************************************************************/
+static Bool isdv4ParseOptions(InputInfoPtr pInfo)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
- return (common->wcmForceDevice == DEVICE_ISDV4) ? 1 : 0;
+ wcmISDV4Data *isdv4data;
+ int baud;
+
+ /* Determine default baud rate */
+ baud = (common->tablet_id == 0x90)? 19200 : 38400;
+
+ baud = xf86SetIntOption(pInfo->options, "BaudRate", baud);
+
+ switch (baud)
+ {
+ case 38400:
+ case 19200:
+ /* xf86OpenSerial() takes the baud rate from the options */
+ xf86ReplaceIntOption(pInfo->options, "BaudRate", baud);
+ break;
+ default:
+ xf86Msg(X_ERROR, "%s: Illegal speed value "
+ "(must be 19200 or 38400).",
+ pInfo->name);
+ return FALSE;
+ }
+
+ if (!common->private)
+ {
+ if (!(common->private = calloc(1, sizeof(wcmISDV4Data))))
+ {
+ xf86Msg(X_ERROR, "%s: failed to alloc backend-specific data.\n",
+ pInfo->name);
+ return FALSE;
+ }
+ isdv4data = common->private;
+ isdv4data->baudrate = baud;
+ isdv4data->tablet_initialized = 0;
+ isdv4data->initialized_devices = 0;
+ }
+
+ return TRUE;
}
/*****************************************************************************
@@ -157,11 +256,12 @@ static Bool isdv4Init(InputInfoPtr pInfo, char* id, float *version)
{
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
+ wcmISDV4Data *isdv4data = common->private;
DBG(1, priv, "initializing ISDV4 tablet\n");
- /* Initial baudrate is 38400 */
- if (xf86SetSerialSpeed(pInfo->fd, common->wcmISDV4Speed) < 0)
+ /* Set baudrate */
+ if (xf86SetSerialSpeed(pInfo->fd, isdv4data->baudrate) < 0)
return !Success;
if(id)
@@ -172,13 +272,6 @@ static Bool isdv4Init(InputInfoPtr pInfo, char* id, float *version)
/*set the model */
common->wcmModel = &isdv4General;
- /* Tablet PC Button is on by default */
- common->wcmTPCButtonDefault = 1;
-
- /* check if TPCButton was turned off by user for stylus */
- if (priv->flags & STYLUS_ID)
- common->wcmTPCButton = xf86SetBoolOption(pInfo->options,
- "TPCButton", common->wcmTPCButtonDefault);
return Success;
}
@@ -188,8 +281,9 @@ static Bool isdv4Init(InputInfoPtr pInfo, char* id, float *version)
static int isdv4Query(InputInfoPtr pInfo, const char* query, char* data)
{
+#ifdef DEBUG
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
- WacomCommonPtr common = priv->common;
+#endif
DBG(1, priv, "Querying ISDV4 tablet\n");
@@ -202,37 +296,15 @@ static int isdv4Query(InputInfoPtr pInfo, const char* query, char* data)
/* Read the control data */
if (!wcmWaitForTablet(pInfo, data, ISDV4_PKGLEN_TPCCTL))
- {
- /* Try 19200 if it is not a touch query */
- if (common->wcmISDV4Speed != 19200 && strcmp(query, ISDV4_TOUCH_QUERY))
- {
- common->wcmISDV4Speed = 19200;
- if (xf86SetSerialSpeed(pInfo->fd, common->wcmISDV4Speed) < 0)
- return !Success;
- return isdv4Query(pInfo, query, data);
- }
- else
- return !Success;
- }
+ return !Success;
/* Control data bit check */
if ( !(data[0] & 0x40) )
{
- /* Try 19200 if it is not a touch query */
- if (common->wcmISDV4Speed != 19200 && strcmp(query, ISDV4_TOUCH_QUERY))
- {
- common->wcmISDV4Speed = 19200;
- if (xf86SetSerialSpeed(pInfo->fd, common->wcmISDV4Speed) < 0)
- return !Success;
- return isdv4Query(pInfo, query, data);
- }
- else
- {
- /* Reread the control data since it may fail the first time */
- wcmWaitForTablet(pInfo, data, ISDV4_PKGLEN_TPCCTL);
- if ( !(data[0] & 0x40) )
- return !Success;
- }
+ /* Reread the control data since it may fail the first time */
+ wcmWaitForTablet(pInfo, data, ISDV4_PKGLEN_TPCCTL);
+ if ( !(data[0] & 0x40) )
+ return !Success;
}
return Success;
@@ -244,18 +316,13 @@ static int isdv4Query(InputInfoPtr pInfo, const char* query, char* data)
static void isdv4InitISDV4(WacomCommonPtr common, const char* id, float version)
{
- /* set parameters */
- common->wcmProtocolLevel = 4;
/* length of a packet */
common->wcmPktLength = ISDV4_PKGLEN_TPCPEN;
- /* digitizer X resolution in points/inch */
- common->wcmResolX = 2540;
- /* digitizer Y resolution in points/inch */
- common->wcmResolY = 2540;
-
- /* no touch */
- common->tablet_id = 0x90;
+ /* digitizer X resolution in points/m */
+ common->wcmResolX = ISDV4_PEN_RESOLUTION;
+ /* digitizer Y resolution in points/m */
+ common->wcmResolY = ISDV4_PEN_RESOLUTION;
/* tilt disabled */
common->wcmFlags &= ~TILT_ENABLED_FLAG;
@@ -270,22 +337,66 @@ static int isdv4GetRanges(InputInfoPtr pInfo)
char data[BUFFER_SIZE];
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
+ wcmISDV4Data *isdv4data = common->private;
int ret = Success;
DBG(2, priv, "getting ISDV4 Ranges\n");
+ if (isdv4data->tablet_initialized)
+ goto out;
+
+ /* Set baudrate to configured value */
+ if (xf86SetSerialSpeed(pInfo->fd, isdv4data->baudrate) < 0)
+ {
+ ret = !Success;
+ goto out;
+ }
+
/* Send query command to the tablet */
ret = isdv4Query(pInfo, ISDV4_QUERY, data);
+ if (ret != Success)
+ {
+ int baud;
+
+ /* Try with the other baudrate */
+ baud = (isdv4data->baudrate == 38400)? 19200 : 38400;
+
+ xf86Msg(X_WARNING, "%s: Query failed with %d baud. Trying %d.\n",
+ pInfo->name, isdv4data->baudrate, baud);
+
+ if (xf86SetSerialSpeed(pInfo->fd, baud) < 0)
+ {
+ ret = !Success;
+ goto out;
+ }
+
+ ret = isdv4Query(pInfo, ISDV4_QUERY, data);
+
+ if (ret == Success) {
+ isdv4data->baudrate = baud;
+ /* xf86OpenSerial() takes the baud rate from the options */
+ xf86ReplaceIntOption(pInfo->options, "BaudRate", baud);
+ }
+
+ }
+
if (ret == Success)
{
ISDV4QueryReply reply;
int rc;
- rc = isdv4ParseQuery(data, sizeof(data), &reply);
+ rc = isdv4ParseQuery((unsigned char*)data, sizeof(data), &reply);
if (rc <= 0)
{
- xf86Msg(X_ERROR, "Error while parsing ISDV4 query.\n");
- return BadAlloc;
+ xf86Msg(X_ERROR, "%s: Error while parsing ISDV4 query.\n",
+ pInfo->name);
+ if (rc == 0)
+ DBG(2, common, "reply or len invalid.\n");
+ else
+ DBG(2, common, "header data corrupt.\n");
+ memdump(pInfo, data, sizeof(reply));
+ ret = BadAlloc;
+ goto out;
}
/* transducer data */
@@ -307,22 +418,29 @@ static int isdv4GetRanges(InputInfoPtr pInfo)
DBG(2, priv, "Pen speed=%d "
"maxX=%d maxY=%d maxZ=%d resX=%d resY=%d \n",
- common->wcmISDV4Speed, common->wcmMaxX, common->wcmMaxY,
+ isdv4data->baudrate, common->wcmMaxX, common->wcmMaxY,
common->wcmMaxZ, common->wcmResolX, common->wcmResolY);
}
/* Touch might be supported. Send a touch query command */
- common->wcmISDV4Speed = 38400;
- if (isdv4Query(pInfo, ISDV4_TOUCH_QUERY, data) == Success)
+ if (isdv4data->baudrate == 38400 &&
+ isdv4Query(pInfo, ISDV4_TOUCH_QUERY, data) == Success)
{
ISDV4TouchQueryReply reply;
int rc;
- rc = isdv4ParseTouchQuery(data, sizeof(data), &reply);
+ rc = isdv4ParseTouchQuery((unsigned char*)data, sizeof(data), &reply);
if (rc <= 0)
{
- xf86Msg(X_ERROR, "Error while parsing ISDV4 touch query.\n");
- return BadAlloc;
+ xf86Msg(X_ERROR, "%s: Error while parsing ISDV4 touch query.\n",
+ pInfo->name);
+ if (rc == 0)
+ DBG(2, common, "reply or len invalid.\n");
+ else
+ DBG(2, common, "header data corrupt.\n");
+ memdump(pInfo, data, sizeof(reply));
+ ret = BadAlloc;
+ goto out;
}
switch (reply.sensor_id)
@@ -364,10 +482,10 @@ static int isdv4GetRanges(InputInfoPtr pInfo)
(common->tablet_id != 0x9F))
{
- xf86Msg(X_WARNING, "WACOM: %s tablet id(%x)"
+ xf86Msg(X_WARNING, "%s: tablet id(%x)"
" mismatch with data id (0x01) \n",
pInfo->name, common->tablet_id);
- return ret;
+ goto out;
}
break;
/* 2FGT */
@@ -375,10 +493,10 @@ static int isdv4GetRanges(InputInfoPtr pInfo)
if ((common->tablet_id != 0xE2) &&
(common->tablet_id != 0xE3))
{
- xf86Msg(X_WARNING, "WACOM: %s tablet id(%x)"
+ xf86Msg(X_WARNING, "%s: tablet id(%x)"
" mismatch with data id (0x03) \n",
pInfo->name, common->tablet_id);
- return ret;
+ goto out;
}
break;
}
@@ -394,25 +512,39 @@ static int isdv4GetRanges(InputInfoPtr pInfo)
(1 << reply.panel_resolution);
if (reply.panel_resolution)
- common->wcmTouchResolX = common->wcmTouchResolY = 10;
+ common->wcmTouchResolX = common->wcmTouchResolY = ISDV4_TOUCH_RESOLUTION;
common->wcmVersion = reply.version;
ret = Success;
DBG(2, priv, "touch speed=%d "
"maxTouchX=%d maxTouchY=%d TouchresX=%d TouchresY=%d \n",
- common->wcmISDV4Speed, common->wcmMaxTouchX,
+ isdv4data->baudrate, common->wcmMaxTouchX,
common->wcmMaxTouchY, common->wcmTouchResolX,
common->wcmTouchResolY);
}
xf86Msg(X_INFO, "%s: serial tablet id 0x%X.\n", pInfo->name, common->tablet_id);
+out:
+ if (ret == Success)
+ {
+ isdv4data->tablet_initialized = 1;
+ isdv4data->initialized_devices++;
+ }
+
return ret;
}
static int isdv4StartTablet(InputInfoPtr pInfo)
{
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+ wcmISDV4Data *isdv4data = common->private;
+
+ if (--isdv4data->initialized_devices)
+ return Success;
+
/* Tell the tablet to start sending coordinates */
if (!wcmWriteWait(pInfo, ISDV4_SAMPLING))
return !Success;
@@ -422,6 +554,12 @@ static int isdv4StartTablet(InputInfoPtr pInfo)
static int isdv4StopTablet(InputInfoPtr pInfo)
{
+#if DEBUG
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+#endif
+ int fd_flags;
+
/* Send stop command to the tablet */
if (!wcmWriteWait(pInfo, ISDV4_STOP))
return !Success;
@@ -430,17 +568,177 @@ static int isdv4StopTablet(InputInfoPtr pInfo)
if (wcmWait(250))
return !Success;
+ /* discard potential data on the line */
+ fd_flags = fcntl(pInfo->fd, F_GETFL);
+ if (fcntl(pInfo->fd, F_SETFL, fd_flags | O_NONBLOCK) == 0)
+ {
+ char buffer[10];
+ while (read(pInfo->fd, buffer, sizeof(buffer)) > 0)
+ DBG(10, common, "discarding garbage data.\n");
+ fcntl(pInfo->fd, F_SETFL, fd_flags);
+ }
+
return Success;
}
+/**
+ * Parse one touch packet.
+ *
+ * @param pInfo The device to parse the packet for
+ * @param data Data read from the device
+ * @param len Data length in bytes
+ * @param[out] ds The device state, modified in place.
+ *
+ * @return The channel number of -1 on error.
+ */
-static int isdv4Parse(InputInfoPtr pInfo, const unsigned char* data, int len)
+static int isdv4ParseTouchPacket(InputInfoPtr pInfo, const unsigned char *data,
+ int len, WacomDeviceState *ds)
{
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
WacomDeviceState* last = &common->wcmChannel[0].valid.state;
WacomDeviceState* lastTemp = &common->wcmChannel[1].valid.state;
+ ISDV4TouchData touchdata;
+ int rc;
+ int channel = 0;
+
+ rc = isdv4ParseTouchData(data, len, common->wcmPktLength, &touchdata);
+ if (rc == -1)
+ {
+ xf86Msg(X_ERROR, "%s: failed to parse touch data.\n",
+ pInfo->name);
+ return -1;
+ }
+
+ ds->x = touchdata.x;
+ ds->y = touchdata.y;
+ ds->proximity = touchdata.status;
+ ds->device_type = TOUCH_ID;
+ ds->device_id = TOUCH_DEVICE_ID;
+
+ if (common->wcmPktLength == ISDV4_PKGLEN_TOUCH2FG)
+ {
+ if (touchdata.finger2.status ||
+ (!touchdata.finger2.status && lastTemp->proximity))
+ {
+ /* Got 2FGT. Send the first one if received */
+ if (ds->proximity || (!ds->proximity && last->proximity))
+ {
+ /* time stamp for 2FGT gesture events */
+ if ((ds->proximity && !last->proximity) ||
+ (!ds->proximity && last->proximity))
+ ds->sample = (int)GetTimeInMillis();
+ wcmEvent(common, channel, ds);
+ }
+
+ channel = 1;
+ ds = &common->wcmChannel[channel].work;
+ RESET_RELATIVE(*ds);
+ ds->x = touchdata.finger2.x;
+ ds->y = touchdata.finger2.y;
+ ds->device_type = TOUCH_ID;
+ ds->device_id = TOUCH_DEVICE_ID;
+ ds->proximity = touchdata.finger2.status;
+ /* time stamp for 2FGT gesture events */
+ if ((ds->proximity && !lastTemp->proximity) ||
+ (!ds->proximity && lastTemp->proximity))
+ ds->sample = (int)GetTimeInMillis();
+ }
+ }
+
+ DBG(8, priv, "MultiTouch %s proximity \n", ds->proximity ? "in" : "out of");
+
+ return channel;
+}
+
+/**
+ * Parse one pen packet.
+ *
+ * @param pInfo The device to parse the packet for
+ * @param data Data read from the device
+ * @param len Data length in bytes
+ * @param[out] ds The device state, modified in place.
+ *
+ * @return The channel number.
+ */
+static int isdv4ParsePenPacket(InputInfoPtr pInfo, const unsigned char *data,
+ int len, WacomDeviceState *ds)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+ WacomDeviceState* last = &common->wcmChannel[0].valid.state;
+ int rc;
+ ISDV4CoordinateData coord;
+ int channel = 0;
+ int cur_type;
+
+ rc = isdv4ParseCoordinateData(data, ISDV4_PKGLEN_TPCPEN, &coord);
+
+ if (rc == -1)
+ {
+ xf86Msg(X_ERROR, "%s: failed to parse coordinate data.\n", pInfo->name);
+ return -1;
+ }
+
+ ds->proximity = coord.proximity;
+
+ /* x and y in "normal" orientetion (wide length is X) */
+ ds->x = coord.x;
+ ds->y = coord.y;
+
+ /* pressure */
+ ds->pressure = coord.pressure;
+
+ /* buttons */
+ ds->buttons = coord.tip | (coord.side << 1) | (coord.eraser << 2);
+
+ /* check which device we have */
+ cur_type = (ds->buttons & 4) ? ERASER_ID : STYLUS_ID;
+
+ /* first time into prox */
+ if (!last->proximity && ds->proximity)
+ ds->device_type = cur_type;
+ /* check on previous proximity */
+ else if (ds->buttons && ds->proximity)
+ {
+ /* we might have been fooled by tip and second
+ * sideswitch when it came into prox */
+ if ((ds->device_type != cur_type) &&
+ (ds->device_type == ERASER_ID))
+ {
+ /* send a prox-out for old device */
+ WacomDeviceState out = { 0 };
+ wcmEvent(common, 0, &out);
+ ds->device_type = cur_type;
+ }
+ }
+
+ ds->device_id = (ds->device_type == ERASER_ID) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID;
+
+ /* don't send button 3 event for eraser
+ * button 1 event will be sent by testing presure level
+ */
+ if (ds->device_type == ERASER_ID && ds->buttons & 4)
+ {
+ ds->buttons = 0;
+ ds->device_id = ERASER_DEVICE_ID;
+ }
+
+ DBG(8, priv, "%s\n",
+ ds->device_type == ERASER_ID ? "ERASER " :
+ ds->device_type == STYLUS_ID ? "STYLUS" : "NONE");
+
+ return channel;
+}
+
+
+static int isdv4Parse(InputInfoPtr pInfo, const unsigned char* data, int len)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+ WacomDeviceState* last = &common->wcmChannel[0].valid.state;
WacomDeviceState* ds;
- int n, cur_type, channel = 0;
+ int n, channel = 0;
DBG(10, common, "\n");
@@ -496,123 +794,14 @@ static int isdv4Parse(InputInfoPtr pInfo, const unsigned char* data, int len)
ds = &common->wcmChannel[channel].work;
RESET_RELATIVE(*ds);
- if (common->wcmPktLength != ISDV4_PKGLEN_TPCPEN) /* a touch */
- {
- ISDV4TouchData touchdata;
- int rc;
+ if (common->wcmPktLength == ISDV4_PKGLEN_TPCPEN)
+ channel = isdv4ParsePenPacket(pInfo, data, len, ds);
+ else /* a touch */
+ channel = isdv4ParseTouchPacket(pInfo, data, len, ds);
- rc = isdv4ParseTouchData(data, len, common->wcmPktLength, &touchdata);
- if (rc == -1)
- {
- xf86Msg(X_ERROR, "%s: failed to parse touch data.\n",
- pInfo->name);
- return 0;
- }
-
-
- ds->x = touchdata.x;
- ds->y = touchdata.y;
- ds->capacity = touchdata.capacity;
- ds->buttons = ds->proximity = touchdata.status;
- ds->device_type = TOUCH_ID;
- ds->device_id = TOUCH_DEVICE_ID;
-
- if (common->wcmPktLength == ISDV4_PKGLEN_TOUCH2FG)
- {
- if (touchdata.finger2.status ||
- (!touchdata.finger2.status && lastTemp->proximity))
- {
- /* Got 2FGT. Send the first one if received */
- if (ds->proximity || (!ds->proximity &&
- last->proximity))
- {
- /* time stamp for 2FGT gesture events */
- if ((ds->proximity && !last->proximity) ||
- (!ds->proximity && last->proximity))
- ds->sample = (int)GetTimeInMillis();
- wcmEvent(common, channel, ds);
- }
-
- channel = 1;
- ds = &common->wcmChannel[channel].work;
- RESET_RELATIVE(*ds);
- ds->x = touchdata.finger2.x;
- ds->y = touchdata.finger2.y;
- ds->device_type = TOUCH_ID;
- ds->device_id = TOUCH_DEVICE_ID;
- ds->proximity = touchdata.finger2.status;
- /* time stamp for 2FGT gesture events */
- if ((ds->proximity && !lastTemp->proximity) ||
- (!ds->proximity && lastTemp->proximity))
- ds->sample = (int)GetTimeInMillis();
- }
- }
-
- DBG(8, priv, "MultiTouch "
- "%s proximity \n", ds->proximity ? "in" : "out of");
- }
- else
- {
- int rc;
- ISDV4CoordinateData coord;
-
- rc = isdv4ParseCoordinateData(data, ISDV4_PKGLEN_TPCPEN, &coord);
-
- if (rc == -1)
- {
- xf86Msg(X_ERROR, "%s: failed to parse coordinate data.\n", pInfo->name);
- return 0;
- }
-
- ds->proximity = coord.proximity;
-
- /* x and y in "normal" orientetion (wide length is X) */
- ds->x = coord.x;
- ds->y = coord.y;
-
- /* pressure */
- ds->pressure = coord.pressure;
-
- /* buttons */
- ds->buttons = coord.tip | (coord.side << 1) | (coord.eraser << 2);
-
- /* check which device we have */
- cur_type = (ds->buttons & 4) ? ERASER_ID : STYLUS_ID;
-
- /* first time into prox */
- if (!last->proximity && ds->proximity)
- ds->device_type = cur_type;
- /* check on previous proximity */
- else if (ds->buttons && ds->proximity)
- {
- /* we might have been fooled by tip and second
- * sideswitch when it came into prox */
- if ((ds->device_type != cur_type) &&
- (ds->device_type == ERASER_ID))
- {
- /* send a prox-out for old device */
- WacomDeviceState out = { 0 };
- wcmEvent(common, 0, &out);
- ds->device_type = cur_type;
- }
- }
-
- ds->device_id = (ds->device_type == ERASER_ID) ?
- ERASER_DEVICE_ID : STYLUS_DEVICE_ID;
-
- /* don't send button 3 event for eraser
- * button 1 event will be sent by testing presure level
- */
- if (ds->device_type == ERASER_ID && ds->buttons&4)
- {
- ds->buttons = 0;
- ds->device_id = ERASER_DEVICE_ID;
- }
+ if (channel < 0)
+ return 0;
- DBG(8, priv, "%s\n",
- ds->device_type == ERASER_ID ? "ERASER " :
- ds->device_type == STYLUS_ID ? "STYLUS" : "NONE");
- }
wcmEvent(common, channel, ds);
return common->wcmPktLength;
}
@@ -681,66 +870,21 @@ static int wcmWaitForTablet(InputInfoPtr pInfo, char* answer, int size)
return maxtry;
}
-/**
- * Query the device's fd for the key bits and the tablet ID. Returns the ID
- * on success or 0 on failure.
- * For serial devices, we set the BTN_TOOL_DOUBLETAP etc. bits based on the
- * device ID. This matching only works for wacom devices (serial ID of
- * WACf), all others are simply assumed to be pen + erasor.
- */
-int isdv4ProbeKeys(InputInfoPtr pInfo)
+static int set_keybits_wacom(int id, unsigned long *keys)
{
- int id, i;
int tablet_id = 0;
- struct serial_struct tmp;
- const char *device = xf86SetStrOption(pInfo->options, "Device", NULL);
- WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
- WacomCommonPtr common = priv->common;
-
- if (ioctl(pInfo->fd, TIOCGSERIAL, &tmp) < 0)
- return 0;
-
- /* check device name for ID first */
- if (sscanf(pInfo->name, "WACf%x", &id) <= 1)
- {
- /* id in file sys/class/tty/%str/device/id */
- FILE *file;
- char sysfs_id[256];
- char *str = strstr(device, "ttyS");
- snprintf(sysfs_id, sizeof(sysfs_id),
- "/sys/class/tty/%s/device/id", str);
- file = fopen(sysfs_id, "r");
-
- /* return true since it falls to default */
- if (file)
- {
- /* make sure we fall to default */
- if (fscanf(file, "WACf%x\n", &id) <= 0)
- id = 0;
-
- fclose(file);
- }
- }
-
- /* we have tried memset. it doesn't work */
- for (i=0; i<NBITS(KEY_MAX); i++)
- common->wcmKeys[i] = 0;
-
- /* default to penabled */
- common->wcmKeys[LONG(BTN_TOOL_PEN)] |= BIT(BTN_TOOL_PEN);
- common->wcmKeys[LONG(BTN_TOOL_RUBBER)] |= BIT(BTN_TOOL_RUBBER);
/* id < 0x008 are only penabled */
if (id > 0x007)
- common->wcmKeys[LONG(BTN_TOOL_DOUBLETAP)] |= BIT(BTN_TOOL_DOUBLETAP);
+ SETBIT(keys, BTN_TOOL_FINGER);
if (id > 0x0a)
- common->wcmKeys[LONG(BTN_TOOL_TRIPLETAP)] |= BIT(BTN_TOOL_TRIPLETAP);
+ SETBIT(keys, BTN_TOOL_DOUBLETAP);
/* no pen 2FGT */
if (id == 0x010)
{
- common->wcmKeys[LONG(BTN_TOOL_PEN)] &= ~BIT(BTN_TOOL_PEN);
- common->wcmKeys[LONG(BTN_TOOL_RUBBER)] &= ~BIT(BTN_TOOL_RUBBER);
+ CLEARBIT(keys, BTN_TOOL_PEN);
+ CLEARBIT(keys, BTN_TOOL_RUBBER);
}
/* 0x9a and 0x9f are only detected by communicating
@@ -748,137 +892,158 @@ int isdv4ProbeKeys(InputInfoPtr pInfo)
* at later stage and true knowledge of capacitive
* support will be delayed until that point.
*/
- if (id >= 0x0 && id <= 0x7)
- tablet_id = 0x90;
- else if (id >= 0x8 && id <= 0xa)
- tablet_id = 0x93;
- else if (id >= 0xb && id <= 0xe)
- tablet_id = 0xe3;
- else if (id == 0x10)
- tablet_id = 0xe2;
+ switch(id)
+ {
+ case 0x0 ... 0x7: tablet_id = 0x90; break;
+ case 0x8 ... 0xa: tablet_id = 0x93; break;
+ case 0xb ... 0xe: tablet_id = 0xe3; break;
+ case 0x10: tablet_id = 0xe2; break;
+ }
return tablet_id;
}
-/* Convert buffer data of buffer sized len into a query reply.
- * Returns the number of bytes read from buffer or 0 if the buffer was on
- * insufficient length. Returns -1 on parsing or internal errors.
- */
-static inline int isdv4ParseQuery(const char *buffer, const size_t len,
- ISDV4QueryReply *reply)
+static int set_keybits_fujitsu(int id, unsigned long *keys)
{
- int header, control;
+ int tablet_id = 0x90; /* default to penabled */
- if (!reply || len < ISDV4_PKGLEN_TPCCTL)
- return 0;
-
- header = !!(buffer[0] & HEADER_BIT);
- control = !!(buffer[0] & CONTROL_BIT);
-
- if (!header || !control)
- return -1;
-
- reply->data_id = buffer[0] & DATA_ID_MASK;
+ if (id == 0x2e7) {
+ SETBIT(keys, BTN_TOOL_DOUBLETAP);
+ tablet_id = 0xe3;
+ }
- /* FIXME: big endian? */
- reply->x_max = (buffer[1] << 9) | (buffer[2] << 2) | ((buffer[6] >> 5) & 0x3);
- reply->y_max = (buffer[3] << 9) | (buffer[4] << 2) | ((buffer[6] >> 3) & 0x3);
- reply->pressure_max = buffer[5] | (buffer[6] & 0x7);
- reply->tilt_y_max = buffer[7];
- reply->tilt_x_max = buffer[8];
- reply->version = buffer[9] << 7 | buffer[10];
+ if (id == 0x2e9) {
+ SETBIT(keys, BTN_TOOL_FINGER);
+ tablet_id = 0x93;
+ }
- return ISDV4_PKGLEN_TPCCTL;
+ return tablet_id;
}
-static inline int isdv4ParseTouchQuery(const char *buffer, const size_t len,
- ISDV4TouchQueryReply *reply)
-{
- int header, control;
+typedef struct {
+ const char *pattern; /* sscanf matching pattern to extract ID */
+ const int vendor_id;
+ /* set the bits in the given keys array based on the id. return the
+ * tablet_id or the closest guess anyway */
+ int (*set_bits)(int id, unsigned long* keys);
+} ISDV4ModelDesc;
- if (!reply || len < ISDV4_PKGLEN_TPCCTL)
- return 0;
+static ISDV4ModelDesc isdv4_models[] = {
+ { "WACf%x", WACOM_VENDOR_ID, set_keybits_wacom },
+ { "FUJ%x", 0 /* FIXME: */, set_keybits_fujitsu },
+ { NULL, 0 }
+};
- header = !!(buffer[0] & HEADER_BIT);
- control = !!(buffer[0] & CONTROL_BIT);
+/**
+ * Query the model number from the sysfs /sys/.../device/id file and return
+ * the matching model and the ID for the model we found.
+ *
+ * @param pInfo Used for debugging purposes only.
+ * @param fd File descriptor to the serial device.
+ * @param[out] id On success, returns the numeric ID for this device
+ * according to the model-specific matching pattern.
+ *
+ * @return The model description for the matching device or NULL if no
+ * matching one could be found.
+ */
+static ISDV4ModelDesc*
+model_from_sysfs(const InputInfoPtr pInfo, int fd, int *id)
+{
+ WacomDevicePtr priv = pInfo->private;
+ ISDV4ModelDesc* model = NULL;
+ struct udev *udev = NULL;
+ struct udev_device *device = NULL;
+ struct stat st;
+ char *sysfs_path = NULL;
+ FILE *file = NULL;
- if (!header || !control)
- return -1;
+ fstat(fd, &st);
- reply->data_id = buffer[0] & DATA_ID_MASK;
- reply->sensor_id = buffer[2] & 0x7;
- reply->panel_resolution = buffer[1];
- /* FIXME: big endian? */
- reply->x_max = (buffer[3] << 9) | (buffer[4] << 2) | ((buffer[2] >> 5) & 0x3);
- reply->y_max = (buffer[5] << 9) | (buffer[6] << 2) | ((buffer[2] >> 3) & 0x3);
- reply->capacity_resolution = buffer[7];
- reply->version = buffer[9] << 7 | buffer[10];
+ udev = udev_new();
+ device = udev_device_new_from_devnum(udev, 'c', st.st_rdev);
- return ISDV4_PKGLEN_TPCCTL;
-}
+ if (!device)
+ goto out;
-/* pktlen defines what touch type we parse */
-static inline int isdv4ParseTouchData(const unsigned char *buffer, const size_t buff_len,
- const size_t pktlen, ISDV4TouchData *touchdata)
-{
- int header, touch;
+ if (asprintf(&sysfs_path, "%s/device/id",
+ udev_device_get_syspath(device)) == -1)
+ goto out;
- if (!touchdata || buff_len < pktlen)
- return 0;
+ DBG(8, priv, "sysfs path: %s\n", sysfs_path);
- header = !!(buffer[0] & HEADER_BIT);
- touch = !!(buffer[0] & TOUCH_CONTROL_BIT);
+ file = fopen(sysfs_path, "r");
+ if (!file)
+ goto out;
- if (header != 1 || touch != 1)
- return -1;
+ model = isdv4_models;
- memset(touchdata, 0, sizeof(*touchdata));
+ while(model->pattern && fscanf(file, model->pattern, id) <= 0)
+ model++;
- touchdata->status = buffer[0] & 0x1;
- /* FIXME: big endian */
- touchdata->x = buffer[1] << 7 | buffer[2];
- touchdata->y = buffer[3] << 7 | buffer[4];
- if (pktlen == ISDV4_PKGLEN_TOUCH9A)
- touchdata->capacity = buffer[5] << 7 | buffer[6];
+ if (!model->pattern)
+ model = NULL;
- if (pktlen == ISDV4_PKGLEN_TOUCH2FG)
- {
- touchdata->finger2.x = buffer[7] << 7 | buffer[8];
- touchdata->finger2.y = buffer[9] << 7 | buffer[10];
- touchdata->finger2.status = !!(buffer[0] & 0x2);
- /* FIXME: is there a fg2 capacity? */
- }
+ DBG(8, priv, "sysfs check found %s:%d\n",
+ (model) ? model->pattern : "<unknown>", *id);
- return pktlen;
+out:
+ udev_device_unref(device);
+ udev_unref(udev);
+ fclose(file);
+ free(sysfs_path);
+ return model;
}
-static inline int isdv4ParseCoordinateData(const unsigned char *buffer, const size_t len,
- ISDV4CoordinateData *coord)
+/**
+ * Query the device's fd for the key bits and the tablet ID. Returns the ID
+ * on success. If the model vendor is unknown, we assume a penabled device
+ * (0x90). If the model vendor is known but the model itself is unknown, the
+ * return value depends on the model-specific matching code (0 for Wacom,
+ * 0x90 for Fujitsu).
+ *
+ * For serial devices, we set the BTN_TOOL_DOUBLETAP etc. bits based on the
+ * device ID. This matching only works for known devices (see the
+ * isdv4_model list), all others are simply assumed to be pen + erasor.
+ */
+static int isdv4ProbeKeys(InputInfoPtr pInfo)
{
- int header, control;
+ int id = 0;
+ int tablet_id = 0x90;
+ struct serial_struct tmp;
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+ ISDV4ModelDesc *model = isdv4_models;
- if (!coord || len < ISDV4_PKGLEN_TPCPEN)
+ if (ioctl(pInfo->fd, TIOCGSERIAL, &tmp) < 0)
return 0;
- header = !!(buffer[0] & HEADER_BIT);
- control = !!(buffer[0] & TOUCH_CONTROL_BIT);
+ /* check device name for ID first */
+ while (model->pattern && sscanf(pInfo->name, model->pattern, &id) < 1)
+ model++;
- if (header != 1 || control != 0)
- return -1;
+ /* grab id from sysfs/.../device/id */
+ if (!model->pattern)
+ model = model_from_sysfs(pInfo, pInfo->fd, &id);
+
+ memset(common->wcmKeys, 0, sizeof(common->wcmKeys));
+
+ /* default to penabled */
+ SETBIT(common->wcmKeys, BTN_TOOL_PEN);
+ SETBIT(common->wcmKeys, BTN_TOOL_RUBBER);
- coord->proximity = (buffer[0] >> 5) & 0x1;
- coord->tip = buffer[0] & 0x1;
- coord->side = (buffer[0] >> 1) & 0x1;
- coord->eraser = (buffer[0] >> 2) & 0x1;
- /* FIXME: big endian */
- coord->x = (buffer[1] << 9) | (buffer[2] << 2) | ((buffer[6] >> 5) & 0x3);
- coord->y = (buffer[3] << 9) | (buffer[4] << 2) | ((buffer[6] >> 3) & 0x3);
+ if (model)
+ {
+ common->vendor_id = model->vendor_id;
+ if (model->set_bits)
+ tablet_id = model->set_bits(id, common->wcmKeys);
+ }
- coord->pressure = ((buffer[6] & 0x7) << 7) | buffer[5];
- coord->tilt_x = buffer[7];
- coord->tilt_y = buffer[8];
+ /* Change to generic protocol to match USB MT format */
+ common->wcmProtocolLevel = WCM_PROTOCOL_GENERIC;
- return ISDV4_PKGLEN_TPCPEN;
+ common->tablet_id = tablet_id;
+
+ return tablet_id;
}
-/* vim: set noexpandtab shiftwidth=8: */
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/wcmISDV4.h b/src/wcmISDV4.h
deleted file mode 100644
index cb1ad1b..0000000
--- a/src/wcmISDV4.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2010 by Red Hat, Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-
-#ifndef WCMISDV4_H
-#define WCMISDV4_H
-
-/* packet length for individual models */
-#define ISDV4_PKGLEN_TOUCH93 5
-#define ISDV4_PKGLEN_TOUCH9A 7
-#define ISDV4_PKGLEN_TPCPEN 9
-#define ISDV4_PKGLEN_TPCCTL 11
-#define ISDV4_PKGLEN_TOUCH2FG 13
-
-#define HEADER_BIT 0x80
-#define CONTROL_BIT 0x40
-#define DATA_ID_MASK 0x3F
-#define TOUCH_CONTROL_BIT 0x10
-
-/* ISDV4 protocol parsing structs. */
-
-/* Query reply data */
-typedef struct {
- unsigned char data_id; /* always 00H */
- uint16_t x_max;
- uint16_t y_max;
- uint16_t pressure_max;
- uint8_t tilt_x_max;
- uint8_t tilt_y_max;
- uint16_t version;
-} ISDV4QueryReply;
-
-/* Touch Query reply data */
-typedef struct {
- uint8_t data_id; /* always 01H */
- uint8_t panel_resolution;
- uint8_t sensor_id;
- uint16_t x_max;
- uint16_t y_max;
- uint8_t capacity_resolution;
- uint16_t version;
-} ISDV4TouchQueryReply;
-
-/* Touch Data format. Note that capacity and finger2 are only set for some
- * devices (0 on all others) */
-typedef struct {
- uint8_t status; /* touch down/up */
- uint16_t x;
- uint16_t y;
- uint16_t capacity;
- struct {
- uint8_t status; /* touch down/up */
- uint16_t x;
- uint16_t y;
- } finger2;
-} ISDV4TouchData;
-
-/* Coordinate data format */
-typedef struct {
- uint8_t proximity; /* in proximity? */
- uint8_t tip; /* tip/eraser pressed? */
- uint8_t side; /* side switch pressed? */
- uint8_t eraser; /* eraser pressed? */
- uint16_t x;
- uint16_t y;
- uint16_t pressure;
- uint8_t tilt_x;
- uint8_t tilt_y;
-} ISDV4CoordinateData;
-
-#endif /* WCMISDV4_H */
-
-/* vim: set noexpandtab shiftwidth=8: */
diff --git a/src/wcmTouchFilter.c b/src/wcmTouchFilter.c
index 69ba5ca..0d1f950 100644
--- a/src/wcmTouchFilter.c
+++ b/src/wcmTouchFilter.c
@@ -1,5 +1,6 @@
/*
* Copyright 2009 - 2010 by Ping Cheng, Wacom. <pingc@wacom.com>
+ * Copyright 2011 by Alexey Osipov. <simba@lerlan.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -21,31 +22,27 @@
#endif
#include "xf86Wacom.h"
+#include "wcmTouchFilter.h"
#include <math.h>
/* Defines for 2FC Gesture */
-#define WACOM_DIST_IN_POINT 300
-#define WACOM_APART_IN_POINT 350
-#define WACOM_MOTION_IN_POINT 50
-#define WACOM_PARA_MOTION_IN_POINT 50
-#define WACOM_DOWN_TIME_IN_MS 800
-#define WACOM_TIME_BETWEEN_IN_MS 400
#define WACOM_HORIZ_ALLOWED 1
#define WACOM_VERT_ALLOWED 2
+#define WACOM_GESTURE_LAG_TIME 10
+#define GESTURE_NONE_MODE 0
#define GESTURE_TAP_MODE 1
#define GESTURE_SCROLL_MODE 2
#define GESTURE_ZOOM_MODE 4
+#define GESTURE_LAG_MODE 8
+#define GESTURE_PREDRAG_MODE 16
+#define GESTURE_DRAG_MODE 32
#define WCM_SCROLL_UP 5 /* vertical up */
#define WCM_SCROLL_DOWN 4 /* vertical down */
#define WCM_SCROLL_LEFT 6 /* horizontal left */
#define WCM_SCROLL_RIGHT 7 /* horizontal right */
-
-/* Defines for Tap Add-a-Finger to Click */
-#define WACOM_TAP_TIME_IN_MS 150
-
static void wcmFingerScroll(WacomDevicePtr priv);
static void wcmFingerZoom(WacomDevicePtr priv);
@@ -57,246 +54,378 @@ static double touchDistance(WacomDeviceState ds0, WacomDeviceState ds1)
return distance;
}
-static Bool pointsInLine(WacomDeviceState ds0, WacomDeviceState ds1,
- int *direction)
+static Bool pointsInLine(WacomCommonPtr common, WacomDeviceState ds0,
+ WacomDeviceState ds1)
{
Bool ret = FALSE;
-
- if (*direction == 0)
+ Bool rotated = common->wcmRotate == ROTATE_CW ||
+ common->wcmRotate == ROTATE_CCW;
+ int horizon_rotated = (rotated) ?
+ WACOM_HORIZ_ALLOWED : WACOM_VERT_ALLOWED;
+ int vertical_rotated = (rotated) ?
+ WACOM_VERT_ALLOWED : WACOM_HORIZ_ALLOWED;
+ int max_spread = common->wcmGestureParameters.wcmMaxScrollFingerSpread;
+
+ if (!common->wcmGestureParameters.wcmScrollDirection)
{
- if (abs(ds0.x - ds1.x) < WACOM_PARA_MOTION_IN_POINT)
+ if ((abs(ds0.x - ds1.x) < max_spread) &&
+ (abs(ds0.y - ds1.y) > max_spread))
{
- *direction = WACOM_VERT_ALLOWED;
+ common->wcmGestureParameters.wcmScrollDirection = horizon_rotated;
ret = TRUE;
}
- else if (abs(ds0.y - ds1.y) < WACOM_PARA_MOTION_IN_POINT)
+ if ((abs(ds0.y - ds1.y) < max_spread) &&
+ (abs(ds0.x - ds1.x) > max_spread))
{
- *direction = WACOM_HORIZ_ALLOWED;
+ common->wcmGestureParameters.wcmScrollDirection = vertical_rotated;
ret = TRUE;
}
}
- else if (*direction == WACOM_HORIZ_ALLOWED)
+ else if (common->wcmGestureParameters.wcmScrollDirection == vertical_rotated)
{
- if (abs(ds0.y - ds1.y) < WACOM_PARA_MOTION_IN_POINT)
+ if (abs(ds0.y - ds1.y) < max_spread)
ret = TRUE;
}
- else if (*direction == WACOM_VERT_ALLOWED)
+ else if (common->wcmGestureParameters.wcmScrollDirection == horizon_rotated)
{
- if (abs(ds0.x - ds1.x) < WACOM_PARA_MOTION_IN_POINT)
+ if (abs(ds0.x - ds1.x) < max_spread)
ret = TRUE;
}
return ret;
}
-static Bool pointsInLineAfter(int p1, int p2)
+/* send a button event */
+static void wcmSendButtonClick(WacomDevicePtr priv, int button, int state)
{
- Bool ret = FALSE;
+ int mode = is_absolute(priv->pInfo);
- if (abs(p1 - p2) < WACOM_PARA_MOTION_IN_POINT)
- ret = TRUE;
+ /* send button event in state */
+ xf86PostButtonEventP(priv->pInfo->dev, mode,button,
+ state,0,0,0);
- return ret;
+ /* We have changed the button state (from down to up) for the device
+ * so we need to update the record */
+ if (button == 1)
+ priv->oldButtons = 0;
}
-static void wcmSwitchLeftClick(WacomDevicePtr priv)
+/*****************************************************************************
+ * translate second finger tap to right click
+ ****************************************************************************/
+
+static void wcmFingerTapToClick(WacomDevicePtr priv)
{
WacomCommonPtr common = priv->common;
+ WacomChannelPtr firstChannel = common->wcmChannel;
+ WacomChannelPtr secondChannel = common->wcmChannel + 1;
+ WacomDeviceState ds[2] = { firstChannel->valid.states[0],
+ secondChannel->valid.states[0] };
+ WacomDeviceState dsLast[2] = { firstChannel->valid.states[1],
+ secondChannel->valid.states[1] };
- if (common->wcmGestureMode)
+ DBG(10, priv, "\n");
+
+ /* process second finger tap if matched */
+ if ((ds[0].sample < ds[1].sample) &&
+ ((GetTimeInMillis() -
+ dsLast[1].sample) <= common->wcmGestureParameters.wcmTapTime) &&
+ !ds[1].proximity && dsLast[1].proximity)
{
- /* send button one up */
- xf86PostButtonEvent(priv->pInfo->dev,
- priv->flags & ABSOLUTE_FLAG,
- 1,0,0,priv->naxes, priv->oldX,
- priv->oldY,0,0,0,0);
- priv->oldButtons = 0;
+ /* send left up before sending right down */
+ wcmSendButtonClick(priv, 1, 0);
+ common->wcmGestureMode = GESTURE_TAP_MODE;
+
+ /* right button down */
+ wcmSendButtonClick(priv, 3, 1);
+
+ /* right button up */
+ wcmSendButtonClick(priv, 3, 0);
}
}
-/*****************************************************************************
- * translate second finger tap to right click
- ****************************************************************************/
+static CARD32 wcmSingleFingerTapTimer(OsTimerPtr timer, CARD32 time, pointer arg)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)arg;
+ WacomCommonPtr common = priv->common;
+
+ if (common->wcmGestureMode == GESTURE_PREDRAG_MODE)
+ {
+ /* left button down */
+ wcmSendButtonClick(priv, 1, 1);
-void wcmFingerTapToClick(WacomCommonPtr common)
+ /* left button up */
+ wcmSendButtonClick(priv, 1, 0);
+ common->wcmGestureMode = GESTURE_NONE_MODE;
+ }
+
+ return 0;
+}
+
+/* A single finger tap is defined as 1 finger tap that lasts less than
+ * wcmTapTime. It results in a left button press.
+ *
+ * Some work must be done to make sure two fingers were not touching
+ * during this gesture. This is easy if first finger is released
+ * first. To handle case of second finger released first, require
+ * second finger to have been released before first finger ever touched.
+ *
+ * Function relies on ds[0/1].sample to be updated only when entering or
+ * exiting proximity so no storage is needed when initial touch occurs.
+ */
+static void wcmSingleFingerTap(WacomDevicePtr priv)
{
- WacomDevicePtr priv = common->wcmDevices;
+ WacomCommonPtr common = priv->common;
WacomChannelPtr firstChannel = common->wcmChannel;
WacomChannelPtr secondChannel = common->wcmChannel + 1;
WacomDeviceState ds[2] = { firstChannel->valid.states[0],
secondChannel->valid.states[0] };
WacomDeviceState dsLast[2] = { firstChannel->valid.states[1],
secondChannel->valid.states[1] };
- int direction = 0;
DBG(10, priv, "\n");
- /* skip initial second finger event */
- if (!dsLast[1].proximity)
- goto skipGesture;
+ /* This gesture is only valid on touchpads. */
+ if (TabletHasFeature(priv->common, WCM_LCD))
+ return;
+
+ if (!ds[0].proximity && dsLast[0].proximity && !ds[1].proximity)
+ {
+ /* Single Tap must have lasted less than wcmTapTime
+ * and second finger must not have released after
+ * first finger touched.
+ */
+ if (ds[0].sample - dsLast[0].sample <=
+ common->wcmGestureParameters.wcmTapTime &&
+ ds[1].sample < dsLast[0].sample)
+ {
+ common->wcmGestureMode = GESTURE_PREDRAG_MODE;
+
+ /* Delay to detect possible drag operation */
+ TimerSet(NULL, 0, common->wcmGestureParameters.wcmTapTime, wcmSingleFingerTapTimer, priv);
+ }
+ }
+}
+
+/* Monitors for 1 finger touch and forces left button press or 1 finger
+ * release and will remove left button press.
+ *
+ * This function relies on wcmGestureMode will only be zero if
+ * WACOM_GESTURE_LAG_TIME has passed and still ony 1 finger on screen.
+ */
+static void wcmSingleFingerPress(WacomDevicePtr priv)
+{
+ WacomCommonPtr common = priv->common;
+ WacomChannelPtr firstChannel = common->wcmChannel;
+ WacomChannelPtr secondChannel = common->wcmChannel + 1;
+ WacomDeviceState ds[2] = { firstChannel->valid.states[0],
+ secondChannel->valid.states[0] };
+
+ DBG(10, priv, "\n");
+
+ /* This gesture is only valid on touchscreens. */
+ if (!TabletHasFeature(priv->common, WCM_LCD))
+ return;
+
+ if (ds[0].proximity && !ds[1].proximity)
+ firstChannel->valid.states[0].buttons |= 1;
+ if (!ds[0].proximity && !ds[1].proximity)
+ firstChannel->valid.states[0].buttons &= ~1;
+}
+
+/* parsing gesture mode according to 2FGT data */
+void wcmGestureFilter(WacomDevicePtr priv, int channel)
+{
+ WacomCommonPtr common = priv->common;
+ WacomChannelPtr firstChannel = common->wcmChannel;
+ WacomChannelPtr secondChannel = common->wcmChannel + 1;
+ WacomDeviceState ds[2] = { firstChannel->valid.states[0],
+ secondChannel->valid.states[0] };
+ WacomDeviceState dsLast[2] = { firstChannel->valid.states[1],
+ secondChannel->valid.states[1] };
+
+ DBG(10, priv, "\n");
if (!IsTouch(priv))
{
- /* go through the shared port */
- for (; priv; priv = priv->next)
- if (IsTouch(priv))
- break;
+ /* this should never happen */
+ xf86Msg(X_ERROR, "WACOM: No touch device found for %s \n",
+ common->device_path);
+ return;
}
- if (priv) /* found the first finger */
+ if (!common->wcmGesture)
+ goto ret;
+
+ /* When 2 fingers are in proximity, it must always be in one of
+ * the valid 2 fingers modes: LAG, SCROLL, or ZOOM.
+ * LAG mode is used while deciding between SCROLL and ZOOM and
+ * prevents cursor movement. Force to LAG mode if ever in NONE
+ * mode to stop cursor movement.
+ */
+ if (ds[0].proximity && ds[1].proximity)
{
- /* allow only second finger tap */
- if ((dsLast[0].sample < dsLast[1].sample) && ((GetTimeInMillis() -
- dsLast[1].sample) <= WACOM_TAP_TIME_IN_MS))
+ if (common->wcmGestureMode == GESTURE_NONE_MODE)
+ common->wcmGestureMode = GESTURE_LAG_MODE;
+ }
+ /* When only 1 finger is in proximity, it can be in either LAG mode,
+ * NONE mode or DRAG mode.
+ * 1 finger LAG mode is a very short time period mainly to debounce
+ * initial touch.
+ * NONE and DRAG mode means cursor is allowed to move around.
+ * DRAG mode in addition means that left button pressed.
+ * There is no need to bother about LAG_TIME while in DRAG mode.
+ * TODO: This has to use dsLast[0] because of later logic that
+ * wants mode to be NONE still when 1st entering proximity.
+ * That could use some re-arranging/cleanup.
+ *
+ */
+ else if (dsLast[0].proximity && common->wcmGestureMode != GESTURE_DRAG_MODE)
+ {
+ CARD32 ms = GetTimeInMillis();
+
+ if ((ms - ds[0].sample) < WACOM_GESTURE_LAG_TIME)
{
- /* send right click when second finger taps within WACOM_TAP_TIMEms
- * and both fingers stay within WACOM_DIST */
- if (!ds[1].proximity && dsLast[1].proximity)
- {
- if (touchDistance(ds[0], dsLast[1]) <= WACOM_DIST_IN_POINT)
- {
- /* send left up before sending right down */
- if (!common->wcmGestureMode)
- {
- common->wcmGestureMode = GESTURE_TAP_MODE;
- wcmSwitchLeftClick(priv);
- }
-
- /* right button down */
- xf86PostButtonEvent(priv->pInfo->dev,
- priv->flags & ABSOLUTE_FLAG,
- 3,1,0,priv->naxes, priv->oldX,
- priv->oldY,0,0,0,0);
- /* right button up */
- xf86PostButtonEvent(priv->pInfo->dev,
- priv->flags & ABSOLUTE_FLAG,
- 3,0,0,priv->naxes, priv->oldX,
- priv->oldY,0,0,0,0);
- }
- }
+ /* Must have recently come into proximity. Change
+ * into LAG mode.
+ */
+ if (common->wcmGestureMode == GESTURE_NONE_MODE)
+ common->wcmGestureMode = GESTURE_LAG_MODE;
}
- else if ((WACOM_TAP_TIME_IN_MS < (GetTimeInMillis() - dsLast[0].sample))
- && (WACOM_TAP_TIME_IN_MS < (GetTimeInMillis() - dsLast[1].sample))
- && ds[0].proximity && ds[1].proximity)
+ else
{
- if (abs(touchDistance(ds[0], ds[1])) >= WACOM_APART_IN_POINT &&
- common->wcmGestureMode != GESTURE_TAP_MODE &&
- common->wcmGestureMode != GESTURE_SCROLL_MODE)
- {
- /* fingers moved apart more than WACOM_APART_IN_POINT
- * zoom mode is entered */
- if (!common->wcmGestureMode)
- {
- common->wcmGestureMode = GESTURE_ZOOM_MODE;
- wcmSwitchLeftClick(priv);
- }
- wcmFingerZoom(priv);
- }
-
- if ( pointsInLine(ds[0], dsLast[0], &direction) &&
- pointsInLine(ds[1], dsLast[1], &direction) &&
- common->wcmGestureMode != GESTURE_ZOOM_MODE &&
- common->wcmGestureMode != GESTURE_TAP_MODE)
- {
- /* send scroll event when both fingers move in
- * the same direction */
- if (!common->wcmGestureMode)
- {
- common->wcmGestureMode = GESTURE_SCROLL_MODE;
- wcmSwitchLeftClick(priv);
- }
- wcmFingerScroll(priv);
- }
+ /* Been in LAG mode long enough. Force to NONE mode. */
+ common->wcmGestureMode = GESTURE_NONE_MODE;
}
}
- else
- /* this should never happen */
- xf86Msg(X_ERROR, "WACOM: No touch device found for %s \n", common->wcmDevice);
-
-skipGesture:
- /* keep the initial in-prox time */
- ds[0].sample = dsLast[0].sample;
- ds[1].sample = dsLast[1].sample;
- /* keep the initial states for both fingers */
- if ( !(common->wcmGestureMode && (GESTURE_SCROLL_MODE | GESTURE_ZOOM_MODE))
- && ds[0].proximity && ds[1].proximity)
+ if (ds[1].proximity && !dsLast[1].proximity)
{
- common->wcmGestureState[0] = ds[0];
+ /* keep the initial states for gesture mode */
common->wcmGestureState[1] = ds[1];
- }
-}
-static void wcmSendVerticalScrollEvent(WacomDevicePtr priv,
- int dist, int up, int dn)
-{
- int i = 0;
+ /* reset the initial count for a new getsure */
+ common->wcmGestureParameters.wcmGestureUsed = 0;
+ }
- for (i=0; i<(int)(((double)abs(dist)/
- (double)WACOM_MOTION_IN_POINT) + 0.5); i++)
+ if (ds[0].proximity && !dsLast[0].proximity)
{
- if (dist > 0)
+ /* keep the initial states for gesture mode */
+ common->wcmGestureState[0] = ds[0];
+
+ /* reset the initial count for a new getsure */
+ common->wcmGestureParameters.wcmGestureUsed = 0;
+
+ /* initialize the cursor position */
+ if (common->wcmGestureMode == GESTURE_NONE_MODE && !channel)
+ goto ret;
+
+ /* got second touch in TapTime interval after first one,
+ * switch to DRAG mode */
+ if (common->wcmGestureMode == GESTURE_PREDRAG_MODE)
{
- /* button down */
- xf86PostButtonEvent(priv->pInfo->dev,
- priv->flags & ABSOLUTE_FLAG,
- up,1,0,priv->naxes, priv->oldX,
- priv->oldY,0,0,0,0);
- /* button up */
- xf86PostButtonEvent(priv->pInfo->dev,
- priv->flags & ABSOLUTE_FLAG,
- up,0,0,priv->naxes, priv->oldX,
- priv->oldY,0,0,0,0);
+ /* left button down */
+ wcmSendButtonClick(priv, 1, 1);
+ common->wcmGestureMode = GESTURE_DRAG_MODE;
+ goto ret;
}
- else
+ }
+
+ if (!ds[0].proximity && !ds[1].proximity)
+ {
+ /* first finger was out-prox when GestureMode was still on */
+ if (!dsLast[0].proximity &&
+ common->wcmGestureMode != GESTURE_NONE_MODE)
+ /* send first finger out prox */
+ wcmSoftOutEvent(priv->pInfo);
+
+ /* if were in DRAG mode, send left button up now */
+ if (common->wcmGestureMode == GESTURE_DRAG_MODE)
+ wcmSendButtonClick(priv, 1, 0);
+
+ /* exit gesture mode when both fingers are out */
+ common->wcmGestureMode = GESTURE_NONE_MODE;
+ common->wcmGestureParameters.wcmScrollDirection = 0;
+
+ goto ret;
+ }
+
+ if (!(common->wcmGestureMode & (GESTURE_SCROLL_MODE | GESTURE_ZOOM_MODE)) && channel)
+ wcmFingerTapToClick(priv);
+
+ /* Change mode happens only when both fingers are out */
+ if (common->wcmGestureMode & GESTURE_TAP_MODE)
+ goto ret;
+
+ /* skip initial finger event for scroll and zoom */
+ if (!dsLast[0].proximity || !dsLast[1].proximity)
+ goto ret;
+
+ /* was in zoom mode no time check needed */
+ if ((common->wcmGestureMode & GESTURE_ZOOM_MODE) &&
+ ds[0].proximity && ds[1].proximity)
+ wcmFingerZoom(priv);
+
+ /* was in scroll mode no time check needed */
+ else if (common->wcmGestureMode & GESTURE_SCROLL_MODE)
+ wcmFingerScroll(priv);
+
+ /* process complex two finger gestures */
+ else {
+ CARD32 ms = GetTimeInMillis();
+ int taptime = common->wcmGestureParameters.wcmTapTime;
+
+ if (ds[0].proximity && ds[1].proximity &&
+ (taptime < (ms - ds[0].sample)) &&
+ (taptime < (ms - ds[1].sample)))
{
- /* button down */
- xf86PostButtonEvent(priv->pInfo->dev,
- priv->flags & ABSOLUTE_FLAG,
- dn,1,0,priv->naxes, priv->oldX,
- priv->oldY,0,0,0,0);
- /* button up */
- xf86PostButtonEvent(priv->pInfo->dev,
- priv->flags & ABSOLUTE_FLAG,
- dn,0,0,priv->naxes, priv->oldX,
- priv->oldY,0,0,0,0);
+ /* scroll should be considered first since it requires
+ * a finger distance check */
+ wcmFingerScroll(priv);
+
+ if (!(common->wcmGestureMode & GESTURE_SCROLL_MODE))
+ wcmFingerZoom(priv);
}
}
+ret:
+ if (common->wcmGestureMode == GESTURE_NONE_MODE && !channel)
+ {
+ /* Since this is in ret block, can not rely on generic
+ * wcmGesture enable check from above.
+ */
+ if (common->wcmGesture)
+ wcmSingleFingerTap(priv);
+ wcmSingleFingerPress(priv);
+ }
}
-static void wcmSendHorizontalScrollEvent(WacomDevicePtr priv,
- int dist, int left, int right)
+static void wcmSendScrollEvent(WacomDevicePtr priv, int dist,
+ int buttonUp, int buttonDn)
{
- int i = 0;
+ int button = (dist > 0) ? buttonUp : buttonDn;
+ WacomCommonPtr common = priv->common;
+ int count = (int)((1.0 * abs(dist)/
+ common->wcmGestureParameters.wcmScrollDistance) + 0.5);
+ WacomChannelPtr firstChannel = common->wcmChannel;
+ WacomChannelPtr secondChannel = common->wcmChannel + 1;
+ WacomDeviceState ds[2] = { firstChannel->valid.states[0],
+ secondChannel->valid.states[0] };
- for (i=0; i<(int)(((double)abs(dist)/
- (double)WACOM_MOTION_IN_POINT) + 0.5); i++)
+ /* user might have changed from up to down or vice versa */
+ if (count < common->wcmGestureParameters.wcmGestureUsed)
{
- if (dist > 0)
- {
- /* button down */
- xf86PostButtonEvent(priv->pInfo->dev,
- priv->flags & ABSOLUTE_FLAG,
- left,1,0,priv->naxes, priv->oldX,
- priv->oldY,0,0,0,0);
- /* button up */
- xf86PostButtonEvent(priv->pInfo->dev,
- priv->flags & ABSOLUTE_FLAG,
- left,0,0,priv->naxes, priv->oldX,
- priv->oldY,0,0,0,0);
- }
- else
- {
- /* button down */
- xf86PostButtonEvent(priv->pInfo->dev,
- priv->flags & ABSOLUTE_FLAG,
- right,1,0,priv->naxes, priv->oldX,
- priv->oldY,0,0,0,0);
- /* button up */
- xf86PostButtonEvent(priv->pInfo->dev,
- priv->flags & ABSOLUTE_FLAG,
- right,0,0,priv->naxes, priv->oldX,
- priv->oldY,0,0,0,0);
- }
+ common->wcmGestureState[0] = ds[0];
+ common->wcmGestureState[1] = ds[1];
+ common->wcmGestureParameters.wcmGestureUsed = 0;
+ return;
+ }
+
+ count -= common->wcmGestureParameters.wcmGestureUsed;
+ common->wcmGestureParameters.wcmGestureUsed += count;
+ while (count--)
+ {
+ wcmSendButtonClick(priv, button, 1);
+ wcmSendButtonClick(priv, button, 0);
+ DBG(10, priv, "loop count = %d \n", count);
}
}
@@ -307,16 +436,39 @@ static void wcmFingerScroll(WacomDevicePtr priv)
WacomChannelPtr secondChannel = common->wcmChannel + 1;
WacomDeviceState ds[2] = { firstChannel->valid.states[0],
secondChannel->valid.states[0] };
- WacomDeviceState dsLast[2] = { firstChannel->valid.states[1],
- secondChannel->valid.states[1] };
+
int midPoint_new = 0;
int midPoint_old = 0;
- int i = 0, dist =0;
- int gesture = 0;
+ int i = 0, dist = 0;
WacomFilterState filterd; /* borrow this struct */
+ int max_spread = common->wcmGestureParameters.wcmMaxScrollFingerSpread;
DBG(10, priv, "\n");
+ if (common->wcmGestureMode != GESTURE_SCROLL_MODE)
+ {
+ if (abs(touchDistance(ds[0], ds[1]) -
+ touchDistance(common->wcmGestureState[0],
+ common->wcmGestureState[1])) < max_spread)
+ {
+ /* two fingers stay close to each other all the time and
+ * move in vertical or horizontal direction together
+ */
+ if (pointsInLine(common, ds[0], common->wcmGestureState[0])
+ && pointsInLine(common, ds[1], common->wcmGestureState[1])
+ && common->wcmGestureParameters.wcmScrollDirection)
+ {
+ /* left button might be down. Send it up first */
+ wcmSendButtonClick(priv, 1, 0);
+ common->wcmGestureMode = GESTURE_SCROLL_MODE;
+ }
+ }
+ }
+
+ /* still not a scroll event yet? */
+ if (common->wcmGestureMode != GESTURE_SCROLL_MODE)
+ return;
+
/* initialize the points so we can rotate them */
filterd.x[0] = ds[0].x;
filterd.y[0] = ds[0].y;
@@ -326,56 +478,54 @@ static void wcmFingerScroll(WacomDevicePtr priv)
filterd.y[2] = common->wcmGestureState[0].y;
filterd.x[3] = common->wcmGestureState[1].x;
filterd.y[3] = common->wcmGestureState[1].y;
- filterd.x[4] = dsLast[0].x;
- filterd.y[4] = dsLast[0].y;
- filterd.x[5] = dsLast[1].x;
- filterd.y[5] = dsLast[1].y;
- /* rotate the coordinates first */
+ /* scrolling has directions so rotation has to be considered first */
for (i=0; i<6; i++)
- wcmRotateCoordinates(priv->pInfo, &filterd.x[i], &filterd.y[i]);
+ wcmRotateAndScaleCoordinates(priv->pInfo, &filterd.x[i], &filterd.y[i]);
/* check vertical direction */
- midPoint_old = (((double)filterd.x[4] + (double)filterd.x[5]) / 2.);
- midPoint_new = (((double)filterd.x[0] + (double)filterd.x[1]) / 2.);
- if (pointsInLineAfter(midPoint_old, midPoint_new))
+ if (common->wcmGestureParameters.wcmScrollDirection == WACOM_VERT_ALLOWED)
{
midPoint_old = (((double)filterd.y[2] + (double)filterd.y[3]) / 2.);
midPoint_new = (((double)filterd.y[0] + (double)filterd.y[1]) / 2.);
- dist = midPoint_old - midPoint_new;
- if (abs(dist) > WACOM_PARA_MOTION_IN_POINT)
+ /* allow one finger scroll */
+ if (!ds[0].proximity)
{
- gesture = 1;
- wcmSendVerticalScrollEvent(priv, dist,
- WCM_SCROLL_UP, WCM_SCROLL_DOWN);
+ midPoint_old = filterd.y[3];
+ midPoint_new = filterd.y[1];
}
- /* check horizontal direction */
- if (!gesture)
+ if (!ds[1].proximity)
{
- midPoint_old = (((double)filterd.y[4] + (double)filterd.y[5]) / 2.);
- midPoint_new = (((double)filterd.y[0] + (double)filterd.y[1]) / 2.);
- if (pointsInLineAfter(midPoint_old, midPoint_new))
- {
- midPoint_old = (((double)filterd.x[2] + (double)filterd.x[3]) / 2.);
- midPoint_new = (((double)filterd.x[0] + (double)filterd.x[1]) / 2.);
- dist = midPoint_old - midPoint_new;
-
- if (abs(dist) > WACOM_PARA_MOTION_IN_POINT)
- {
- gesture = 1;
- wcmSendHorizontalScrollEvent(priv, dist,
- WCM_SCROLL_LEFT, WCM_SCROLL_RIGHT);
- }
- }
+ midPoint_old = filterd.y[2];
+ midPoint_new = filterd.y[0];
+ }
+
+ dist = midPoint_old - midPoint_new;
+ wcmSendScrollEvent(priv, dist, WCM_SCROLL_UP, WCM_SCROLL_DOWN);
+ }
+
+ if (common->wcmGestureParameters.wcmScrollDirection == WACOM_HORIZ_ALLOWED)
+ {
+ midPoint_old = (((double)filterd.x[2] + (double)filterd.x[3]) / 2.);
+ midPoint_new = (((double)filterd.x[0] + (double)filterd.x[1]) / 2.);
+
+ /* allow one finger scroll */
+ if (!ds[0].proximity)
+ {
+ midPoint_old = filterd.x[3];
+ midPoint_new = filterd.x[1];
}
- if (gesture)
+
+ if (!ds[1].proximity)
{
- /* reset initial states */
- common->wcmGestureState[0] = ds[0];
- common->wcmGestureState[1] = ds[1];
+ midPoint_old = filterd.x[2];
+ midPoint_new = filterd.x[0];
}
+
+ dist = midPoint_old - midPoint_new;
+ wcmSendScrollEvent(priv, dist, WCM_SCROLL_RIGHT, WCM_SCROLL_LEFT);
}
}
@@ -386,30 +536,69 @@ static void wcmFingerZoom(WacomDevicePtr priv)
WacomChannelPtr secondChannel = common->wcmChannel + 1;
WacomDeviceState ds[2] = { firstChannel->valid.states[0],
secondChannel->valid.states[0] };
- int i = 0;
+ int count, button;
int dist = touchDistance(common->wcmGestureState[0],
common->wcmGestureState[1]);
+ int max_spread = common->wcmGestureParameters.wcmMaxScrollFingerSpread;
DBG(10, priv, "\n");
+ if (common->wcmGestureMode != GESTURE_ZOOM_MODE)
+ {
+ /* two fingers moved apart from each other */
+ if (abs(touchDistance(ds[0], ds[1]) -
+ touchDistance(common->wcmGestureState[0],
+ common->wcmGestureState[1])) >
+ (3 * max_spread))
+ {
+ /* left button might be down, send it up first */
+ wcmSendButtonClick(priv, 1, 0);
+
+ /* fingers moved apart more than 3 times
+ * wcmMaxScrollFingerSpread, zoom mode is entered */
+ common->wcmGestureMode = GESTURE_ZOOM_MODE;
+ }
+ }
+
+ if (common->wcmGestureMode != GESTURE_ZOOM_MODE)
+ return;
+
dist = touchDistance(ds[0], ds[1]) - dist;
+ count = (int)((1.0 * abs(dist)/common->wcmGestureParameters.wcmZoomDistance) + 0.5);
- /* zooming? */
- if (abs(dist) > WACOM_MOTION_IN_POINT)
+ /* user might have changed from left to right or vice versa */
+ if (count < common->wcmGestureParameters.wcmGestureUsed)
{
- /* FIXME: this hardcodes the positions of ctrl to the ones on
- the us keyboard layout. Tough luck. The alternative is to run
- through the XKB table and figure out where it's hiding. Good
- luck. Gesture support is not supposed to be in the driver...
- */
- wcmEmitKeycode (priv->pInfo->dev, 37 /*XK_Control_L*/, 1);
- wcmSendVerticalScrollEvent(priv, dist, 4, 5);
- wcmEmitKeycode (priv->pInfo->dev, 37 /*XK_Control_L*/, 0);
-
- /* reset initial states */
+ /* reset the initial states for the new getsure */
common->wcmGestureState[0] = ds[0];
common->wcmGestureState[1] = ds[1];
+ common->wcmGestureParameters.wcmGestureUsed = 0;
+ return;
+ }
+
+ /* zooming? Send ctrl + scroll up/down event.
+ FIXME: this hardcodes the positions of ctrl and assumes that ctrl is
+ actually a modifier. Tough luck. The alternative is to run through
+ the XKB table and figure out where the key for the ctrl modifier is
+ hiding. Good luck.
+ Gesture support is not supposed to be in the driver...
+ */
+ button = (dist > 0) ? 4 : 5;
+
+ count -= common->wcmGestureParameters.wcmGestureUsed;
+ common->wcmGestureParameters.wcmGestureUsed += count;
+ while (count--)
+ {
+ wcmEmitKeycode (priv->pInfo->dev, 37 /*XK_Control_L*/, 1);
+ wcmSendButtonClick (priv, button, 1);
+ wcmSendButtonClick (priv, button, 0);
+ wcmEmitKeycode (priv->pInfo->dev, 37 /*XK_Control_L*/, 0);
}
}
-/* vim: set noexpandtab shiftwidth=8: */
+Bool wcmTouchNeedSendEvents(WacomCommonPtr common)
+{
+ return !(common->wcmGestureMode & ~GESTURE_DRAG_MODE);
+}
+
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/wcmTouchFilter.h b/src/wcmTouchFilter.h
new file mode 100644
index 0000000..331abde
--- /dev/null
+++ b/src/wcmTouchFilter.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2009 - 2010 by Ping Cheng, Wacom. <pingc@wacom.com>
+ * Copyright 2011 by Alexey Osipov. <simba@lerlan.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XF86_WCMTOUCHFILTER_H
+#define __XF86_WCMTOUCHFILTER_H
+
+#include "xf86Wacom.h"
+
+/****************************************************************************/
+
+void wcmGestureFilter(WacomDevicePtr priv, int channel);
+Bool wcmTouchNeedSendEvents(WacomCommonPtr common);
+
+/****************************************************************************/
+#endif /* __XF86_WCMTOUCHFILTER_H */
diff --git a/src/wcmUSB.c b/src/wcmUSB.c
index 23649db..8451c25 100644
--- a/src/wcmUSB.c
+++ b/src/wcmUSB.c
@@ -22,23 +22,30 @@
#endif
#include "xf86Wacom.h"
-#include "wcmFilter.h"
#include <asm/types.h>
#include <linux/input.h>
#include <sys/utsname.h>
-
-#ifndef BTN_TASK
-#define BTN_TASK 0x117
-#endif
-
-#ifndef BTN_TOOL_TRIPLETAP
-#define BTN_TOOL_TRIPLETAP 0x14e
-#endif
+#include <linux/version.h>
+
+#define MAX_USB_EVENTS 32
+
+typedef struct {
+ int wcmLastToolSerial;
+ int wcmBTNChannel;
+ int wcmDeviceType;
+ Bool wcmPenTouch;
+ Bool wcmUseMT;
+ int wcmMTChannel;
+ int wcmPrevChannel;
+ int wcmEventCnt;
+ struct input_event wcmEvents[MAX_USB_EVENTS];
+} wcmUSBData;
static Bool usbDetect(InputInfoPtr);
static Bool usbWcmInit(InputInfoPtr pDev, char* id, float *version);
-
+static int usbProbeKeys(InputInfoPtr pInfo);
+static int usbStart(InputInfoPtr pInfo);
static void usbInitProtocol5(WacomCommonPtr common, const char* id,
float version);
static void usbInitProtocol4(WacomCommonPtr common, const char* id,
@@ -48,242 +55,50 @@ static int usbParse(InputInfoPtr pInfo, const unsigned char* data, int len);
static int usbDetectConfig(InputInfoPtr pInfo);
static void usbParseEvent(InputInfoPtr pInfo,
const struct input_event* event);
-static void usbParseChannel(InputInfoPtr pInfo, int channel);
-static int usbChooseChannel(WacomCommonPtr common, int serial);
+static void usbParseSynEvent(InputInfoPtr pInfo,
+ const struct input_event *event);
+static void usbDispatchEvents(InputInfoPtr pInfo);
+static int usbChooseChannel(WacomCommonPtr common);
WacomDeviceClass gWacomUSBDevice =
{
usbDetect,
+ NULL, /* no USB-specific options */
usbWcmInit,
+ usbProbeKeys
};
- static WacomModel usbUnknown =
- {
- "Unknown USB",
- usbInitProtocol5, /* assume the best */
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- NULL, /* input filtering not needed */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbPenPartner =
- {
- "USB PenPartner",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterCoord, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbGraphire =
- {
- "USB Graphire",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterCoord, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbGraphire2 =
- {
- "USB Graphire2",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterCoord, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbGraphire3 =
- {
- "USB Graphire3",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterCoord, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbGraphire4 =
- {
- "USB Graphire4",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterCoord, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbBamboo =
- {
- "USB Bamboo",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterCoord, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbBamboo1 =
- {
- "USB Bamboo1",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterCoord, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbBambooFun =
- {
- "USB BambooFun",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterCoord, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbCintiq =
- {
- "USB PL/Cintiq",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- NULL, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbCintiqPartner =
- {
- "USB CintiqPartner",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- NULL, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbIntuos =
- {
- "USB Intuos1",
- usbInitProtocol5,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterIntuos, /* input filtering recommended */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbIntuos2 =
- {
- "USB Intuos2",
- usbInitProtocol5,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterIntuos, /* input filtering recommended */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbIntuos3 =
- {
- "USB Intuos3",
- usbInitProtocol5,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterIntuos, /* input filtering recommended */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbIntuos4 =
- {
- "USB Intuos4",
- usbInitProtocol5,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterIntuos, /* input filtering recommended */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbVolito =
- {
- "USB Volito",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterCoord, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbVolito2 =
- {
- "USB Volito2",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterCoord, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
-
- static WacomModel usbCintiqV5 =
- {
- "USB CintiqV5",
- usbInitProtocol5,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- wcmFilterIntuos, /* input filtering recommended */
- usbDetectConfig, /* detect hardware buttons etc */
- };
+#define DEFINE_MODEL(mname, identifier, protocol) \
+static struct _WacomModel mname = \
+{ \
+ .name = identifier, \
+ .Initialize = usbInitProtocol##protocol,\
+ .GetResolution = NULL, \
+ .GetRanges = usbWcmGetRanges, \
+ .Start = usbStart, \
+ .Parse = usbParse, \
+ .DetectConfig = usbDetectConfig, \
+};
- static WacomModel usbTabletPC =
- {
- "USB TabletPC",
- usbInitProtocol4,
- NULL, /* resolution not queried */
- usbWcmGetRanges,
- NULL, /* start not supported */
- usbParse,
- NULL, /* input filtering */
- usbDetectConfig, /* detect hardware buttons etc */
- };
+DEFINE_MODEL(usbUnknown, "Unknown USB", 5)
+DEFINE_MODEL(usbPenPartner, "USB PenPartner", 4);
+DEFINE_MODEL(usbGraphire, "USB Graphire", 4);
+DEFINE_MODEL(usbGraphire2, "USB Graphire2", 4);
+DEFINE_MODEL(usbGraphire3, "USB Graphire3", 4);
+DEFINE_MODEL(usbGraphire4, "USB Graphire4", 4);
+DEFINE_MODEL(usbBamboo, "USB Bamboo", 4);
+DEFINE_MODEL(usbBamboo1, "USB Bamboo1", 4);
+DEFINE_MODEL(usbBambooFun, "USB BambooFun", 4);
+DEFINE_MODEL(usbCintiq, "USB PL/Cintiq", 4);
+DEFINE_MODEL(usbCintiqPartner, "USB CintiqPartner", 4);
+DEFINE_MODEL(usbIntuos, "USB Intuos1", 5);
+DEFINE_MODEL(usbIntuos2, "USB Intuos2", 5);
+DEFINE_MODEL(usbIntuos3, "USB Intuos3", 5);
+DEFINE_MODEL(usbIntuos4, "USB Intuos4", 5);
+DEFINE_MODEL(usbVolito, "USB Volito", 4);
+DEFINE_MODEL(usbVolito2, "USB Volito2", 4);
+DEFINE_MODEL(usbCintiqV5, "USB CintiqV5", 5);
+DEFINE_MODEL(usbTabletPC, "USB TabletPC", 4);
/*****************************************************************************
* usbDetect --
@@ -307,23 +122,33 @@ static Bool usbDetect(InputInfoPtr pInfo)
xf86Msg(X_ERROR, "%s: usbDetect: can not ioctl version\n", pInfo->name);
return 0;
}
-#ifdef EVIOCGRAB
- /* Try to grab the event device so that data don't leak to /dev/input/mice */
- SYSCALL(err = ioctl(pInfo->fd, EVIOCGRAB, (pointer)1));
- if (err < 0)
- xf86Msg(X_ERROR, "%s: Wacom X driver can't grab event device, errno=%d\n",
- pInfo->name, errno);
-#endif
return 1;
}
/*****************************************************************************
- * wcmusbInit --
+ * usbStart --
****************************************************************************/
+static int
+usbStart(InputInfoPtr pInfo)
+{
+ int err;
+
+#ifdef EVIOCGRAB
+ /* Try to grab the event device so that data don't leak to /dev/input/mice */
+ SYSCALL(err = ioctl(pInfo->fd, EVIOCGRAB, (pointer)1));
+
+ /* this is called for all tools, so all but the first one fails with
+ * EBUSY */
+ if (err < 0 && errno != EBUSY)
+ xf86Msg(X_ERROR, "%s: Wacom X driver can't grab event device (%s)\n",
+ pInfo->name, strerror(errno));
+#endif
+ return Success;
+}
/* Key codes used to mark tablet buttons -- must be in sync
- * with the keycode array in wacom.c kernel driver.
+ * with the keycode array in wacom kernel drivers.
*/
static unsigned short padkey_codes [] = {
BTN_0, BTN_1, BTN_2, BTN_3, BTN_4,
@@ -334,100 +159,159 @@ static unsigned short padkey_codes [] = {
BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_SELECT
};
+/* Fixed mapped stylus and mouse buttons */
+
+#define WCM_USB_MAX_MOUSE_BUTTONS 5
+#define WCM_USB_MAX_STYLUS_BUTTONS 3
+
+static unsigned short mouse_codes [] = {
+ BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, BTN_BACK, BTN_FORWARD,
+ BTN_SIDE, BTN_EXTRA
+};
+
static struct
{
- unsigned char model_id;
- int yRes; /* tablet Y resolution in points/inch */
- int xRes; /* tablet X resolution in points/inch */
+ const unsigned int vendor_id;
+ const unsigned int model_id;
+ int yRes; /* tablet Y resolution in units/meter */
+ int xRes; /* tablet X resolution in units/meter */
WacomModelPtr model;
} WacomModelDesc [] =
{
- { 0x00, 1000, 1000, &usbPenPartner }, /* PenPartner */
- { 0x10, 2032, 2032, &usbGraphire }, /* Graphire */
- { 0x11, 2032, 2032, &usbGraphire2 }, /* Graphire2 4x5 */
- { 0x12, 2032, 2032, &usbGraphire2 }, /* Graphire2 5x7 */
- { 0x13, 2032, 2032, &usbGraphire3 }, /* Graphire3 4x5 */
- { 0x14, 2032, 2032, &usbGraphire3 }, /* Graphire3 6x8 */
- { 0x15, 2032, 2032, &usbGraphire4 }, /* Graphire4 4x5 */
- { 0x16, 2032, 2032, &usbGraphire4 }, /* Graphire4 6x8 */
- { 0x17, 2540, 2540, &usbBambooFun }, /* BambooFun 4x5 */
- { 0x18, 2540, 2540, &usbBambooFun }, /* BambooFun 6x8 */
- { 0x19, 2032, 2032, &usbBamboo1 }, /* Bamboo1 Medium*/
- { 0x81, 2032, 2032, &usbGraphire4 }, /* Graphire4 6x8 BlueTooth */
-
- { 0xD1, 2540, 2540, &usbBamboo }, /* CTL-460 */
- { 0xD4, 2540, 2540, &usbBamboo }, /* CTH-461 */
- { 0xD3, 2540, 2540, &usbBamboo }, /* CTL-660 */
- { 0xD2, 2540, 2540, &usbBamboo }, /* CTL-461/S */
- { 0xD0, 2540, 2540, &usbBamboo }, /* Bamboo Touch */
-
- { 0x20, 2540, 2540, &usbIntuos }, /* Intuos 4x5 */
- { 0x21, 2540, 2540, &usbIntuos }, /* Intuos 6x8 */
- { 0x22, 2540, 2540, &usbIntuos }, /* Intuos 9x12 */
- { 0x23, 2540, 2540, &usbIntuos }, /* Intuos 12x12 */
- { 0x24, 2540, 2540, &usbIntuos }, /* Intuos 12x18 */
-
- { 0x03, 508, 508, &usbCintiqPartner }, /* PTU600 */
-
- { 0x30, 508, 508, &usbCintiq }, /* PL400 */
- { 0x31, 508, 508, &usbCintiq }, /* PL500 */
- { 0x32, 508, 508, &usbCintiq }, /* PL600 */
- { 0x33, 508, 508, &usbCintiq }, /* PL600SX */
- { 0x34, 508, 508, &usbCintiq }, /* PL550 */
- { 0x35, 508, 508, &usbCintiq }, /* PL800 */
- { 0x37, 508, 508, &usbCintiq }, /* PL700 */
- { 0x38, 508, 508, &usbCintiq }, /* PL510 */
- { 0x39, 508, 508, &usbCintiq }, /* PL710 */
- { 0xC0, 508, 508, &usbCintiq }, /* DTF720 */
- { 0xC2, 508, 508, &usbCintiq }, /* DTF720a */
- { 0xC4, 508, 508, &usbCintiq }, /* DTF521 */
- { 0xC7, 2540, 2540, &usbCintiq }, /* DTU1931 */
- { 0xCE, 2540, 2540, &usbCintiq }, /* DTU2231 */
- { 0xF0, 2540, 2540, &usbCintiq }, /* DTU1631 */
-
- { 0x41, 2540, 2540, &usbIntuos2 }, /* Intuos2 4x5 */
- { 0x42, 2540, 2540, &usbIntuos2 }, /* Intuos2 6x8 */
- { 0x43, 2540, 2540, &usbIntuos2 }, /* Intuos2 9x12 */
- { 0x44, 2540, 2540, &usbIntuos2 }, /* Intuos2 12x12 */
- { 0x45, 2540, 2540, &usbIntuos2 }, /* Intuos2 12x18 */
- { 0x47, 2540, 2540, &usbIntuos2 }, /* Intuos2 6x8 */
-
- { 0x60, 1016, 1016, &usbVolito }, /* Volito */
-
- { 0x61, 1016, 1016, &usbVolito2 }, /* PenStation */
- { 0x62, 1016, 1016, &usbVolito2 }, /* Volito2 4x5 */
- { 0x63, 1016, 1016, &usbVolito2 }, /* Volito2 2x3 */
- { 0x64, 1016, 1016, &usbVolito2 }, /* PenPartner2 */
-
- { 0x65, 2540, 2540, &usbBamboo }, /* Bamboo */
- { 0x69, 1012, 1012, &usbBamboo1 }, /* Bamboo1 */
-
- { 0xB0, 5080, 5080, &usbIntuos3 }, /* Intuos3 4x5 */
- { 0xB1, 5080, 5080, &usbIntuos3 }, /* Intuos3 6x8 */
- { 0xB2, 5080, 5080, &usbIntuos3 }, /* Intuos3 9x12 */
- { 0xB3, 5080, 5080, &usbIntuos3 }, /* Intuos3 12x12 */
- { 0xB4, 5080, 5080, &usbIntuos3 }, /* Intuos3 12x19 */
- { 0xB5, 5080, 5080, &usbIntuos3 }, /* Intuos3 6x11 */
- { 0xB7, 5080, 5080, &usbIntuos3 }, /* Intuos3 4x6 */
-
- { 0xB8, 5080, 5080, &usbIntuos4 }, /* Intuos4 4x6 */
- { 0xB9, 5080, 5080, &usbIntuos4 }, /* Intuos4 6x9 */
- { 0xBA, 5080, 5080, &usbIntuos4 }, /* Intuos4 8x13 */
- { 0xBB, 5080, 5080, &usbIntuos4 }, /* Intuos4 12x19*/
- { 0xBC, 5080, 5080, &usbIntuos4 }, /* Intuos4 WL USB Endpoint */
- { 0xBD, 5080, 5080, &usbIntuos4 }, /* Intuos4 WL Bluetooth Endpoint */
-
- { 0x3F, 5080, 5080, &usbCintiqV5 }, /* Cintiq 21UX */
- { 0xC5, 5080, 5080, &usbCintiqV5 }, /* Cintiq 20WSX */
- { 0xC6, 5080, 5080, &usbCintiqV5 }, /* Cintiq 12WX */
- { 0xCC, 5080, 5080, &usbCintiqV5 }, /* Cintiq 21UX2 */
-
- { 0x90, 2540, 2540, &usbTabletPC }, /* TabletPC 0x90 */
- { 0x93, 2540, 2540, &usbTabletPC }, /* TabletPC 0x93 */
- { 0x9A, 2540, 2540, &usbTabletPC }, /* TabletPC 0x9A */
- { 0x9F, 10, 10, &usbTabletPC }, /* CapPlus 0x9F */
- { 0xE2, 10, 10, &usbTabletPC }, /* TabletPC 0xE2 */
- { 0xE3, 2540, 2540, &usbTabletPC } /* TabletPC 0xE3 */
+ { WACOM_VENDOR_ID, 0x00, 39370, 39370, &usbPenPartner }, /* PenPartner */
+ { WACOM_VENDOR_ID, 0x10, 80000, 80000, &usbGraphire }, /* Graphire */
+ { WACOM_VENDOR_ID, 0x11, 80000, 80000, &usbGraphire2 }, /* Graphire2 4x5 */
+ { WACOM_VENDOR_ID, 0x12, 80000, 80000, &usbGraphire2 }, /* Graphire2 5x7 */
+ { WACOM_VENDOR_ID, 0x13, 80000, 80000, &usbGraphire3 }, /* Graphire3 4x5 */
+ { WACOM_VENDOR_ID, 0x14, 80000, 80000, &usbGraphire3 }, /* Graphire3 6x8 */
+ { WACOM_VENDOR_ID, 0x15, 80000, 80000, &usbGraphire4 }, /* Graphire4 4x5 */
+ { WACOM_VENDOR_ID, 0x16, 80000, 80000, &usbGraphire4 }, /* Graphire4 6x8 */
+ { WACOM_VENDOR_ID, 0x17, 100000, 100000, &usbBambooFun }, /* BambooFun 4x5 */
+ { WACOM_VENDOR_ID, 0x18, 100000, 100000, &usbBambooFun }, /* BambooFun 6x8 */
+ { WACOM_VENDOR_ID, 0x19, 80000, 80000, &usbBamboo1 }, /* Bamboo1 Medium*/
+ { WACOM_VENDOR_ID, 0x81, 80000, 80000, &usbGraphire4 }, /* Graphire4 6x8 BlueTooth */
+
+ { WACOM_VENDOR_ID, 0xD1, 100000, 100000, &usbBamboo }, /* CTL-460 */
+ { WACOM_VENDOR_ID, 0xD4, 100000, 100000, &usbBamboo }, /* CTH-461 */
+ { WACOM_VENDOR_ID, 0xD3, 100000, 100000, &usbBamboo }, /* CTL-660 */
+ { WACOM_VENDOR_ID, 0xD2, 100000, 100000, &usbBamboo }, /* CTL-461/S */
+ { WACOM_VENDOR_ID, 0xD0, 100000, 100000, &usbBamboo }, /* Bamboo Touch */
+ { WACOM_VENDOR_ID, 0xD6, 100000, 100000, &usbBamboo }, /* CTH-460/K */
+ { WACOM_VENDOR_ID, 0xD7, 100000, 100000, &usbBamboo }, /* CTH-461/S */
+ { WACOM_VENDOR_ID, 0xD8, 100000, 100000, &usbBamboo }, /* CTH-661/S1 */
+ { WACOM_VENDOR_ID, 0xDA, 100000, 100000, &usbBamboo }, /* CTH-461/L */
+ { WACOM_VENDOR_ID, 0xDB, 100000, 100000, &usbBamboo }, /* CTH-661/L */
+
+ { WACOM_VENDOR_ID, 0x20, 100000, 100000, &usbIntuos }, /* Intuos 4x5 */
+ { WACOM_VENDOR_ID, 0x21, 100000, 100000, &usbIntuos }, /* Intuos 6x8 */
+ { WACOM_VENDOR_ID, 0x22, 100000, 100000, &usbIntuos }, /* Intuos 9x12 */
+ { WACOM_VENDOR_ID, 0x23, 100000, 100000, &usbIntuos }, /* Intuos 12x12 */
+ { WACOM_VENDOR_ID, 0x24, 100000, 100000, &usbIntuos }, /* Intuos 12x18 */
+
+ { WACOM_VENDOR_ID, 0x03, 20000, 20000, &usbCintiqPartner }, /* PTU600 */
+
+ { WACOM_VENDOR_ID, 0x30, 20000, 20000, &usbCintiq }, /* PL400 */
+ { WACOM_VENDOR_ID, 0x31, 20000, 20000, &usbCintiq }, /* PL500 */
+ { WACOM_VENDOR_ID, 0x32, 20000, 20000, &usbCintiq }, /* PL600 */
+ { WACOM_VENDOR_ID, 0x33, 20000, 20000, &usbCintiq }, /* PL600SX */
+ { WACOM_VENDOR_ID, 0x34, 20000, 20000, &usbCintiq }, /* PL550 */
+ { WACOM_VENDOR_ID, 0x35, 20000, 20000, &usbCintiq }, /* PL800 */
+ { WACOM_VENDOR_ID, 0x37, 20000, 20000, &usbCintiq }, /* PL700 */
+ { WACOM_VENDOR_ID, 0x38, 20000, 20000, &usbCintiq }, /* PL510 */
+ { WACOM_VENDOR_ID, 0x39, 20000, 20000, &usbCintiq }, /* PL710 */
+ { WACOM_VENDOR_ID, 0xC0, 20000, 20000, &usbCintiq }, /* DTF720 */
+ { WACOM_VENDOR_ID, 0xC2, 20000, 20000, &usbCintiq }, /* DTF720a */
+ { WACOM_VENDOR_ID, 0xC4, 20000, 20000, &usbCintiq }, /* DTF521 */
+ { WACOM_VENDOR_ID, 0xC7, 100000, 100000, &usbCintiq }, /* DTU1931 */
+ { WACOM_VENDOR_ID, 0xCE, 100000, 100000, &usbCintiq }, /* DTU2231 */
+ { WACOM_VENDOR_ID, 0xF0, 100000, 100000, &usbCintiq }, /* DTU1631 */
+
+ { WACOM_VENDOR_ID, 0x41, 100000, 100000, &usbIntuos2 }, /* Intuos2 4x5 */
+ { WACOM_VENDOR_ID, 0x42, 100000, 100000, &usbIntuos2 }, /* Intuos2 6x8 */
+ { WACOM_VENDOR_ID, 0x43, 100000, 100000, &usbIntuos2 }, /* Intuos2 9x12 */
+ { WACOM_VENDOR_ID, 0x44, 100000, 100000, &usbIntuos2 }, /* Intuos2 12x12 */
+ { WACOM_VENDOR_ID, 0x45, 100000, 100000, &usbIntuos2 }, /* Intuos2 12x18 */
+ { WACOM_VENDOR_ID, 0x47, 100000, 100000, &usbIntuos2 }, /* Intuos2 6x8 */
+
+ { WACOM_VENDOR_ID, 0x60, 50000, 50000, &usbVolito }, /* Volito */
+
+ { WACOM_VENDOR_ID, 0x61, 50000, 50000, &usbVolito2 }, /* PenStation */
+ { WACOM_VENDOR_ID, 0x62, 50000, 50000, &usbVolito2 }, /* Volito2 4x5 */
+ { WACOM_VENDOR_ID, 0x63, 50000, 50000, &usbVolito2 }, /* Volito2 2x3 */
+ { WACOM_VENDOR_ID, 0x64, 50000, 50000, &usbVolito2 }, /* PenPartner2 */
+
+ { WACOM_VENDOR_ID, 0x65, 100000, 100000, &usbBamboo }, /* Bamboo */
+ { WACOM_VENDOR_ID, 0x69, 39842, 39842, &usbBamboo1 }, /* Bamboo1 */
+ { WACOM_VENDOR_ID, 0x6A, 100000, 100000, &usbBamboo1 }, /* Bamboo1 4x6 */
+ { WACOM_VENDOR_ID, 0x6B, 100000, 100000, &usbBamboo1 }, /* Bamboo1 5x8 */
+
+ { WACOM_VENDOR_ID, 0xB0, 200000, 200000, &usbIntuos3 }, /* Intuos3 4x5 */
+ { WACOM_VENDOR_ID, 0xB1, 200000, 200000, &usbIntuos3 }, /* Intuos3 6x8 */
+ { WACOM_VENDOR_ID, 0xB2, 200000, 200000, &usbIntuos3 }, /* Intuos3 9x12 */
+ { WACOM_VENDOR_ID, 0xB3, 200000, 200000, &usbIntuos3 }, /* Intuos3 12x12 */
+ { WACOM_VENDOR_ID, 0xB4, 200000, 200000, &usbIntuos3 }, /* Intuos3 12x19 */
+ { WACOM_VENDOR_ID, 0xB5, 200000, 200000, &usbIntuos3 }, /* Intuos3 6x11 */
+ { WACOM_VENDOR_ID, 0xB7, 200000, 200000, &usbIntuos3 }, /* Intuos3 4x6 */
+
+ { WACOM_VENDOR_ID, 0xB8, 200000, 200000, &usbIntuos4 }, /* Intuos4 4x6 */
+ { WACOM_VENDOR_ID, 0xB9, 200000, 200000, &usbIntuos4 }, /* Intuos4 6x9 */
+ { WACOM_VENDOR_ID, 0xBA, 200000, 200000, &usbIntuos4 }, /* Intuos4 8x13 */
+ { WACOM_VENDOR_ID, 0xBB, 200000, 200000, &usbIntuos4 }, /* Intuos4 12x19*/
+ { WACOM_VENDOR_ID, 0xBC, 200000, 200000, &usbIntuos4 }, /* Intuos4 WL USB Endpoint */
+ { WACOM_VENDOR_ID, 0xBD, 200000, 200000, &usbIntuos4 }, /* Intuos4 WL Bluetooth Endpoint */
+
+ { WACOM_VENDOR_ID, 0x3F, 200000, 200000, &usbCintiqV5 }, /* Cintiq 21UX */
+ { WACOM_VENDOR_ID, 0xC5, 200000, 200000, &usbCintiqV5 }, /* Cintiq 20WSX */
+ { WACOM_VENDOR_ID, 0xC6, 200000, 200000, &usbCintiqV5 }, /* Cintiq 12WX */
+ { WACOM_VENDOR_ID, 0xCC, 200000, 200000, &usbCintiqV5 }, /* Cintiq 21UX2 */
+ { WACOM_VENDOR_ID, 0xF4, 200000, 200000, &usbCintiqV5 }, /* Cintiq 24HD */
+
+ { WACOM_VENDOR_ID, 0x90, 100000, 100000, &usbTabletPC }, /* TabletPC 0x90 */
+ { WACOM_VENDOR_ID, 0x93, 100000, 100000, &usbTabletPC }, /* TabletPC 0x93 */
+ { WACOM_VENDOR_ID, 0x97, 100000, 100000, &usbTabletPC }, /* TabletPC 0x97 */
+ { WACOM_VENDOR_ID, 0x9A, 100000, 100000, &usbTabletPC }, /* TabletPC 0x9A */
+ { WACOM_VENDOR_ID, 0x9F, 100000, 100000, &usbTabletPC }, /* CapPlus 0x9F */
+ { WACOM_VENDOR_ID, 0xE2, 100000, 100000, &usbTabletPC }, /* TabletPC 0xE2 */
+ { WACOM_VENDOR_ID, 0xE3, 100000, 100000, &usbTabletPC }, /* TabletPC 0xE3 */
+ { WACOM_VENDOR_ID, 0xE6, 100000, 100000, &usbTabletPC }, /* TabletPC 0xE6 */
+
+ /* IDs from Waltop's driver, available http://www.waltop.com.tw/download.asp?lv=0&id=2.
+ Accessed 8 Apr 2010, driver release date 2009/08/11, fork of linuxwacom 0.8.4.
+ Some more info would be nice for the ID's below... */
+ { WALTOP_VENDOR_ID, 0x24, 80000, 80000, &usbGraphire },
+ { WALTOP_VENDOR_ID, 0x25, 80000, 80000, &usbGraphire2 },
+ { WALTOP_VENDOR_ID, 0x26, 80000, 80000, &usbGraphire2 },
+ { WALTOP_VENDOR_ID, 0x27, 80000, 80000, &usbGraphire3 },
+ { WALTOP_VENDOR_ID, 0x28, 80000, 80000, &usbGraphire3 },
+ { WALTOP_VENDOR_ID, 0x30, 80000, 80000, &usbGraphire4 },
+ { WALTOP_VENDOR_ID, 0x31, 80000, 80000, &usbGraphire4 },
+ { WALTOP_VENDOR_ID, 0x32, 100000, 100000, &usbBambooFun },
+ { WALTOP_VENDOR_ID, 0x33, 100000, 100000, &usbBambooFun },
+ { WALTOP_VENDOR_ID, 0x34, 80000, 80000, &usbBamboo1 },
+ { WALTOP_VENDOR_ID, 0x35, 80000, 80000, &usbGraphire4 },
+ { WALTOP_VENDOR_ID, 0x36, 80000, 80000, &usbGraphire4 },
+ { WALTOP_VENDOR_ID, 0x37, 80000, 80000, &usbGraphire4 },
+ { WALTOP_VENDOR_ID, 0x38, 100000, 100000, &usbBambooFun },
+ { WALTOP_VENDOR_ID, 0x39, 100000, 100000, &usbBambooFun },
+ { WALTOP_VENDOR_ID, 0x51, 100000, 100000, &usbBamboo },
+ { WALTOP_VENDOR_ID, 0x52, 100000, 100000, &usbBamboo },
+
+ { WALTOP_VENDOR_ID, 0x53, 100000, 100000, &usbBamboo },
+ { WALTOP_VENDOR_ID, 0x54, 100000, 100000, &usbBamboo },
+ { WALTOP_VENDOR_ID, 0x55, 100000, 100000, &usbBamboo },
+ { WALTOP_VENDOR_ID, 0x56, 100000, 100000, &usbBamboo },
+ { WALTOP_VENDOR_ID, 0x57, 100000, 100000, &usbBamboo },
+ { WALTOP_VENDOR_ID, 0x58, 100000, 100000, &usbBamboo },
+ { WALTOP_VENDOR_ID, 0x500, 100000, 100000, &usbBamboo },
+ { WALTOP_VENDOR_ID, 0x501, 100000, 100000, &usbBamboo },
+ { WALTOP_VENDOR_ID, 0x502, 200000, 200000, &usbIntuos4 },
+ { WALTOP_VENDOR_ID, 0x503, 200000, 200000, &usbIntuos4 },
+
+ /* N-Trig devices */
+ { NTRIG_VENDOR_ID, 0x01, 44173, 36772, &usbTabletPC },
+
+ /* Add in Lenovo W700 Palmrest digitizer */
+ { LENOVO_VENDOR_ID, 0x6004, 2540, 2540, &usbTabletPC } /* Pen-only */
};
static Bool usbWcmInit(InputInfoPtr pInfo, char* id, float *version)
@@ -438,29 +322,29 @@ static Bool usbWcmInit(InputInfoPtr pInfo, char* id, float *version)
WacomCommonPtr common = priv->common;
DBG(1, priv, "initializing USB tablet\n");
+
+ if (!common->private &&
+ !(common->private = calloc(1, sizeof(wcmUSBData))))
+ {
+ xf86Msg(X_ERROR, "%s: unable to alloc event queue.\n",
+ pInfo->name);
+ return !Success;
+ }
+
*version = 0.0;
/* fetch vendor, product, and model name */
ioctl(pInfo->fd, EVIOCGID, &sID);
ioctl(pInfo->fd, EVIOCGNAME(sizeof(id)), id);
- /* vendor is wacom */
- if (sID.vendor == WACOM_VENDOR_ID)
+ for (i = 0; i < ARRAY_SIZE(WacomModelDesc); i++)
{
- for (i = 0; i < sizeof (WacomModelDesc) / sizeof (WacomModelDesc [0]); i++)
- if (sID.product == WacomModelDesc [i].model_id)
- {
- common->wcmModel = WacomModelDesc [i].model;
- common->wcmResolX = WacomModelDesc [i].xRes;
- common->wcmResolY = WacomModelDesc [i].yRes;
- }
-
- if (common->wcmModel &&
- strstr(common->wcmModel->name, "TabletPC"))
+ if (sID.vendor == WacomModelDesc[i].vendor_id &&
+ sID.product == WacomModelDesc [i].model_id)
{
- /* For penabled Tablet PCs, Tablet PC Button
- * are on by default */
- common->wcmTPCButtonDefault = 1;
+ common->wcmModel = WacomModelDesc [i].model;
+ common->wcmResolX = WacomModelDesc [i].xRes;
+ common->wcmResolY = WacomModelDesc [i].yRes;
}
}
@@ -470,30 +354,37 @@ static Bool usbWcmInit(InputInfoPtr pInfo, char* id, float *version)
common->wcmResolX = common->wcmResolY = 1016;
}
- /* check if TPCButton was turned off in xorg.conf for pen */
- if (priv->flags & STYLUS_ID)
- common->wcmTPCButton = xf86SetBoolOption(pInfo->options,
- "TPCButton", common->wcmTPCButtonDefault);
-
- /* Find out supported button codes - except mouse button codes
- * BTN_LEFT and BTN_RIGHT, which are always fixed. */
+ /* Find out supported button codes. */
common->npadkeys = 0;
- for (i = 0; i < sizeof (padkey_codes) / sizeof (padkey_codes [0]); i++)
+ for (i = 0; i < ARRAY_SIZE(padkey_codes); i++)
if (ISBITSET (common->wcmKeys, padkey_codes [i]))
common->padkey_code [common->npadkeys++] = padkey_codes [i];
- if (ISBITSET (common->wcmKeys, BTN_TASK))
- common->nbuttons = 10;
- else if (ISBITSET (common->wcmKeys, BTN_BACK))
- common->nbuttons = 9;
- else if (ISBITSET (common->wcmKeys, BTN_FORWARD))
- common->nbuttons = 8;
- else if (ISBITSET (common->wcmKeys, BTN_EXTRA))
- common->nbuttons = 7;
- else if (ISBITSET (common->wcmKeys, BTN_SIDE))
- common->nbuttons = 6;
+ if (!(ISBITSET (common->wcmKeys, BTN_TOOL_MOUSE)))
+ {
+ /* If mouse buttons detected but no mouse tool
+ * then they must be associated with pad buttons.
+ */
+ for (i = ARRAY_SIZE(mouse_codes); i > 0; i--)
+ if (ISBITSET(common->wcmKeys, mouse_codes[i]))
+ break;
+
+ /* Make sure room for fixed map mouse buttons. This
+ * means mappings may overlap with padkey_codes[].
+ */
+ if (i != 0 && common->npadkeys < WCM_USB_MAX_MOUSE_BUTTONS)
+ common->npadkeys = WCM_USB_MAX_MOUSE_BUTTONS;
+ }
+
+ /* nbuttons tracks maximum buttons on all tools (stylus/mouse).
+ *
+ * Mouse support left, middle, right, side, and extra side button.
+ * Stylus support tip and 2 stlyus buttons.
+ */
+ if (ISBITSET (common->wcmKeys, BTN_TOOL_MOUSE))
+ common->nbuttons = WCM_USB_MAX_MOUSE_BUTTONS;
else
- common->nbuttons = 5;
+ common->nbuttons = WCM_USB_MAX_STYLUS_BUTTONS;
return Success;
}
@@ -501,31 +392,53 @@ static Bool usbWcmInit(InputInfoPtr pInfo, char* id, float *version)
static void usbInitProtocol5(WacomCommonPtr common, const char* id,
float version)
{
- common->wcmProtocolLevel = 5;
+ common->wcmProtocolLevel = WCM_PROTOCOL_5;
common->wcmPktLength = sizeof(struct input_event);
- common->wcmCursorProxoutDistDefault
- = PROXOUT_INTUOS_DISTANCE;
+ common->wcmCursorProxoutDistDefault = PROXOUT_INTUOS_DISTANCE;
/* tilt enabled */
common->wcmFlags |= TILT_ENABLED_FLAG;
-
- /* reinitialize max here since 0 is for Graphire series */
- common->wcmMaxCursorDist = 256;
-
}
static void usbInitProtocol4(WacomCommonPtr common, const char* id,
float version)
{
- common->wcmProtocolLevel = 4;
+ common->wcmProtocolLevel = WCM_PROTOCOL_4;
common->wcmPktLength = sizeof(struct input_event);
- common->wcmCursorProxoutDistDefault
- = PROXOUT_GRAPHIRE_DISTANCE;
+ common->wcmCursorProxoutDistDefault = PROXOUT_GRAPHIRE_DISTANCE;
/* tilt disabled */
common->wcmFlags &= ~TILT_ENABLED_FLAG;
}
+/* Initialize fixed PAD channel's state to in proximity.
+ *
+ * Some, but not all, Wacom protocol 4/5 devices are always in proximity.
+ * Because of evdev filtering, there will never be a BTN_TOOL_FINGER
+ * sent to initialize state.
+ * Generic protocol devices never send anything to help initialize PAD
+ * device as well.
+ * This helps those 2 cases and does not hurt the cases where kernel
+ * driver sends out-of-proximity event for PAD since PAD is always on
+ * its own channel, PAD_CHANNEL.
+ */
+static void usbWcmInitPadState(InputInfoPtr pInfo)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+ WacomDeviceState *ds;
+ int channel = PAD_CHANNEL;
+
+ DBG(6, common, "Initializing PAD channel %d\n", channel);
+
+ ds = &common->wcmChannel[channel].work;
+
+ ds->proximity = 1;
+ ds->device_type = PAD_ID;
+ ds->device_id = PAD_DEVICE_ID;
+ ds->serial_num = channel;
+}
+
int usbWcmGetRanges(InputInfoPtr pInfo)
{
struct input_absinfo absinfo;
@@ -533,6 +446,7 @@ int usbWcmGetRanges(InputInfoPtr pInfo)
unsigned long abs[NBITS(ABS_MAX)] = {0};
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
+ wcmUSBData* private = common->private;
int is_touch = IsTouch(priv);
/* Devices such as Bamboo P&T may have Pad data reported in the same
@@ -540,7 +454,7 @@ int usbWcmGetRanges(InputInfoPtr pInfo)
* requires it to act the same as Touch.
*/
if (ISBITSET(common->wcmKeys, BTN_TOOL_DOUBLETAP)
- && ISBITSET(common->wcmKeys, BTN_TOOL_FINGER))
+ && ISBITSET(common->wcmKeys, BTN_FORWARD))
is_touch = 1;
if (ioctl(pInfo->fd, EVIOCGBIT(0 /*EV*/, sizeof(ev)), ev) < 0)
@@ -549,16 +463,14 @@ int usbWcmGetRanges(InputInfoPtr pInfo)
return !Success;
}
- common->wcmFlags |= USE_SYN_REPORTS_FLAG;
-
- if (ioctl(pInfo->fd, EVIOCGBIT(EV_ABS,sizeof(abs)),abs) < 0)
+ if (!ISBITSET(ev,EV_ABS))
{
- xf86Msg(X_ERROR, "%s: unable to ioctl abs bits.\n", pInfo->name);
+ xf86Msg(X_ERROR, "%s: no abs bits.\n", pInfo->name);
return !Success;
}
/* absolute values */
- if (!ISBITSET(ev,EV_ABS))
+ if (ioctl(pInfo->fd, EVIOCGBIT(EV_ABS, sizeof(abs)), abs) < 0)
{
xf86Msg(X_ERROR, "%s: unable to ioctl max values.\n", pInfo->name);
return !Success;
@@ -573,14 +485,30 @@ int usbWcmGetRanges(InputInfoPtr pInfo)
if (absinfo.maximum <= 0)
{
- xf86Msg(X_ERROR, "%s: xmax value is wrong.\n", pInfo->name);
+ xf86Msg(X_ERROR, "%s: xmax value is %d, expected > 0.\n",
+ pInfo->name, absinfo.maximum);
return !Success;
}
+
if (!is_touch)
+ {
common->wcmMaxX = absinfo.maximum;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)
+ if (absinfo.resolution > 0)
+ common->wcmResolX = absinfo.resolution * 1000;
+#endif
+ }
else
+ {
common->wcmMaxTouchX = absinfo.maximum;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)
+ if (absinfo.resolution > 0)
+ common->wcmTouchResolX = absinfo.resolution * 1000;
+#endif
+ }
+
/* max y */
if (ioctl(pInfo->fd, EVIOCGABS(ABS_Y), &absinfo) < 0)
{
@@ -590,59 +518,81 @@ int usbWcmGetRanges(InputInfoPtr pInfo)
if (absinfo.maximum <= 0)
{
- xf86Msg(X_ERROR, "%s: ymax value is wrong.\n", pInfo->name);
+ xf86Msg(X_ERROR, "%s: ymax value is %d, expected > 0.\n",
+ pInfo->name, absinfo.maximum);
return !Success;
}
+
if (!is_touch)
+ {
common->wcmMaxY = absinfo.maximum;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)
+ if (absinfo.resolution > 0)
+ common->wcmResolY = absinfo.resolution * 1000;
+#endif
+ }
else
+ {
common->wcmMaxTouchY = absinfo.maximum;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)
+ if (absinfo.resolution > 0)
+ common->wcmTouchResolY = absinfo.resolution * 1000;
+#endif
+ }
+
/* max finger strip X for tablets with Expresskeys
- * or touch physical X for TabletPCs with touch */
- if (ioctl(pInfo->fd, EVIOCGABS(ABS_RX), &absinfo) == 0)
+ * or physical X for touch devices in hundredths of a mm */
+ if (ISBITSET(abs, ABS_RX) &&
+ !ioctl(pInfo->fd, EVIOCGABS(ABS_RX), &absinfo))
{
if (is_touch)
- common->wcmTouchResolX = absinfo.maximum;
+ common->wcmTouchResolX =
+ (int)(((double)common->wcmMaxTouchX * 10.0
+ / (double)absinfo.maximum) + 0.5);
else
common->wcmMaxStripX = absinfo.maximum;
}
/* max finger strip Y for tablets with Expresskeys
- * or touch physical Y for TabletPCs with touch */
- if (ioctl(pInfo->fd, EVIOCGABS(ABS_RY), &absinfo) == 0)
+ * or physical Y for touch devices in hundredths of a mm */
+ if (ISBITSET(abs, ABS_RY) &&
+ !ioctl(pInfo->fd, EVIOCGABS(ABS_RY), &absinfo))
{
if (is_touch)
- common->wcmTouchResolY = absinfo.maximum;
+ common->wcmTouchResolY =
+ (int)(((double)common->wcmMaxTouchY * 10.0
+ / (double)absinfo.maximum) + 0.5);
else
common->wcmMaxStripY = absinfo.maximum;
}
- if (is_touch && common->wcmTouchResolX && common->wcmMaxTouchX)
- {
- common->wcmTouchResolX = (int)(((double)common->wcmTouchResolX)
- / ((double)common->wcmMaxTouchX) + 0.5);
- common->wcmTouchResolY = (int)(((double)common->wcmTouchResolY)
- / ((double)common->wcmMaxTouchY) + 0.5);
-
- if (!common->wcmTouchResolX || !common->wcmTouchResolY)
- {
- xf86Msg(X_ERROR, "%s: touch resolution value(s) was wrong TouchResolX"
- " = %d MaxTouchY = %d.\n", pInfo->name, common->wcmTouchResolX,
- common->wcmTouchResolY);
- return !Success;
- }
-
- }
-
/* max z cannot be configured */
- if (ioctl(pInfo->fd, EVIOCGABS(ABS_PRESSURE), &absinfo) == 0)
+ if (ISBITSET(abs, ABS_PRESSURE) &&
+ !ioctl(pInfo->fd, EVIOCGABS(ABS_PRESSURE), &absinfo))
common->wcmMaxZ = absinfo.maximum;
/* max distance */
- if (ioctl(pInfo->fd, EVIOCGABS(ABS_DISTANCE), &absinfo) == 0)
+ if (ISBITSET(abs, ABS_DISTANCE) &&
+ !ioctl(pInfo->fd, EVIOCGABS(ABS_DISTANCE), &absinfo))
common->wcmMaxDist = absinfo.maximum;
+ if (ISBITSET(abs, ABS_MT_SLOT))
+ {
+ private->wcmUseMT = 1;
+
+ /* pen and MT on the same logical port */
+ if (ISBITSET(common->wcmKeys, BTN_TOOL_PEN))
+ private->wcmPenTouch = TRUE;
+ }
+
+ /* A generic protocol device does not report ABS_MISC event */
+ if (!ISBITSET(abs, ABS_MISC))
+ common->wcmProtocolLevel = WCM_PROTOCOL_GENERIC;
+
+ usbWcmInitPadState(pInfo);
+
return Success;
}
@@ -675,24 +625,85 @@ static int usbParse(InputInfoPtr pInfo, const unsigned char* data, int len)
return common->wcmPktLength;
}
-static int usbChooseChannel(WacomCommonPtr common, int serial)
+/* Up to MAX_CHANNEL tools can be tracked concurrently by driver.
+ * Chose a channel to use to track current batch of events.
+ */
+static int usbChooseChannel(WacomCommonPtr common)
{
/* figure out the channel to use based on serial number */
int i, channel = -1;
+ wcmUSBData* private = common->private;
+ unsigned int serial = private->wcmLastToolSerial;
- if (common->wcmProtocolLevel == 4)
+ if (common->wcmProtocolLevel == WCM_PROTOCOL_GENERIC)
{
- /* Protocol 4 doesn't support tool serial numbers */
+ /* Generic Protocol devices do not use any form of
+ * serial #'s to multiplex events over a single input
+ * and so can always map to channel 0. This means
+ * only 1 tool can ever been in proximity at one time
+ * (MT events are special case handled elsewhere).
+ * It also means all buttons must be associated with
+ * a single tool and can not send tablet buttons
+ * as part of a pad tool.
+ */
+ channel = 0;
+ serial = 1;
+
+ /* Generic devices need to map stylus buttons to "channel"
+ * and all other button presses to PAD. Hardcode PAD
+ * channel here.
+ */
+ private->wcmBTNChannel = PAD_CHANNEL;
+ }
+ else if (common->wcmProtocolLevel == WCM_PROTOCOL_4)
+ {
+ /* Protocol 4 devices support only 2 devices being
+ * in proximity at the same time. This includes
+ * the PAD device as well as 1 other tool
+ * (stylus, mouse, finger touch, etc).
+ * There is a special case of Tablet PC that also
+ * suport a 3rd tool (2nd finger touch) to also be
+ * in proximity at same time but this should eventually
+ * go away when its switched to MT events to fix loss of
+ * events.
+ *
+ * Protocol 4 send fixed serial numbers along with events.
+ * Events associated with PAD device
+ * will send serial number of 0xf0 always.
+ * Events associated with BTN_TOOL_TRIPLETAP (2nd finger
+ * touch) send a serial number of 0x02 always.
+ * Events associated with all other BTN_TOOL_*'s will
+ * either send a serial # of 0x01 or we can act as if
+ * they did send that value.
+ *
+ * Since its a fixed mapping, directly convert this to
+ * channels 0 to 2 with last channel always used for
+ * pad devices.
+ */
if (serial == 0xf0)
- channel = 1;
+ channel = PAD_CHANNEL;
+ else if (serial)
+ channel = serial-1;
else
channel = 0;
+ /* All events go to same channel for Protocol 4 */
+ private->wcmBTNChannel = channel;
}
else if (serial) /* serial number should never be 0 for V5 devices */
{
- /* dual input is supported */
- if ( strstr(common->wcmModel->name, "Intuos1") ||
- strstr(common->wcmModel->name, "Intuos2") )
+ /* Protocol 5 devices can support tracking 2 or 3
+ * tools at once. One is the PAD device
+ * as well as a stylus and/or mouse.
+ *
+ * Events associated with PAD device
+ * will send serial number of -1 (0xffffffff) always.
+ * Events associated with all other BTN_TOOL_*'s will
+ * send a dynamic serial #.
+ *
+ * Logic here is related to dynamically mapping
+ * serial numbers to a fixed channel #.
+ */
+ if (TabletHasFeature(common, WCM_DUALINPUT))
{
/* find existing channel */
for (i=0; i<MAX_CHANNELS; ++i)
@@ -727,6 +738,8 @@ static int usbChooseChannel(WacomCommonPtr common, int serial)
!common->wcmChannel[0].work.proximity ) /* new transducer */
channel = 0;
}
+ /* All events go to same channel for Protocol 5 */
+ private->wcmBTNChannel = channel;
}
/* fresh out of channels */
@@ -750,7 +763,7 @@ static int usbChooseChannel(WacomCommonPtr common, int serial)
serial, (int)GetTimeInMillis());
}
else
- common->wcmLastToolSerial = serial;
+ private->wcmLastToolSerial = serial;
return channel;
}
@@ -758,26 +771,44 @@ static int usbChooseChannel(WacomCommonPtr common, int serial)
static void usbParseEvent(InputInfoPtr pInfo,
const struct input_event* event)
{
- int channel;
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
+ wcmUSBData* private = common->private;
DBG(10, common, "\n");
+
/* store events until we receive the MSC_SERIAL containing
- * the serial number; without it we cannot determine the
- * correct channel. */
+ * the serial number or a SYN_REPORT.
+ */
/* space left? bail if not. */
- if (common->wcmEventCnt >=
- (sizeof(common->wcmEvents)/sizeof(*common->wcmEvents)))
+ if (private->wcmEventCnt >= ARRAY_SIZE(private->wcmEvents))
{
xf86Msg(X_ERROR, "%s: usbParse: Exceeded event queue (%d) \n",
- pInfo->name, common->wcmEventCnt);
- goto skipEvent;
+ pInfo->name, private->wcmEventCnt);
+ private->wcmEventCnt = 0;
+ return;
}
/* save it for later */
- common->wcmEvents[common->wcmEventCnt++] = *event;
+ private->wcmEvents[private->wcmEventCnt++] = *event;
+
+ if (event->type == EV_MSC || event->type == EV_SYN)
+ usbParseSynEvent(pInfo, event);
+}
+
+/**
+ * EV_SYN marks the end of a set of events containing axes and button info.
+ * Check for valid data and hand over to dispatch to extract the actual
+ * values and process them. At this point, all events up to the EV_SYN are
+ * queued up in wcmEvents.
+ */
+static void usbParseSynEvent(InputInfoPtr pInfo,
+ const struct input_event *event)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+ wcmUSBData* private = common->private;
if ((event->type == EV_MSC) && (event->code == MSC_SERIAL))
{
@@ -791,27 +822,13 @@ static void usbParseEvent(InputInfoPtr pInfo,
}
/* save the serial number so we can look up the channel number later */
- common->wcmLastToolSerial = event->value;
+ private->wcmLastToolSerial = event->value;
- /* if SYN_REPORT is end of record indicator, we are done */
- if (USE_SYN_REPORTS(common))
- return;
+ return;
- /* fall through to deliver the X event */
} else if ((event->type == EV_SYN) && (event->code == SYN_REPORT))
{
- /* if we got a SYN_REPORT but weren't expecting one, change over to
- using SYN_REPORT as the end of record indicator */
- if (! USE_SYN_REPORTS(common))
- {
- xf86Msg(X_ERROR, "%s: Got unexpected SYN_REPORT, changing mode\n",
- pInfo->name);
-
- /* we can expect SYN_REPORT's from now on */
- common->wcmFlags |= USE_SYN_REPORTS_FLAG;
- }
-
- /* end of record. fall through to deliver the X event */
+ /* end of record. fall through to dispatch */
}
else
{
@@ -820,234 +837,739 @@ static void usbParseEvent(InputInfoPtr pInfo,
}
/* ignore events without information */
- if ((common->wcmEventCnt <= 2) && common->wcmLastToolSerial)
+ if ((private->wcmEventCnt < 2) && private->wcmLastToolSerial)
{
DBG(3, common, "%s: dropping empty event"
- " for serial %d\n", pInfo->name, common->wcmLastToolSerial);
+ " for serial %d\n", pInfo->name,
+ private->wcmLastToolSerial);
goto skipEvent;
}
- channel = usbChooseChannel(common, common->wcmLastToolSerial);
+ /* ignore sync windows that contain no data */
+ if (private->wcmEventCnt == 1 &&
+ private->wcmEvents->type == EV_SYN) {
+ DBG(6, common, "no real events received\n");
+ goto skipEvent;
+ }
- /* couldn't decide channel? invalid data */
- if (channel == -1) goto skipEvent;
+ /* dispatch all queued events */
+ usbDispatchEvents(pInfo);
- if (!common->wcmChannel[channel].work.proximity)
+skipEvent:
+ private->wcmEventCnt = 0;
+}
+
+static int usbFilterEvent(WacomCommonPtr common, struct input_event *event)
+{
+ wcmUSBData* private = common->private;
+
+ /* For devices that report multitouch, the following list is a set of
+ * duplicate data from one slot and needs to be filtered out.
+ */
+ if (private->wcmUseMT)
{
- memset(&common->wcmChannel[channel],0,sizeof(WacomChannel));
- /* in case the in-prox event was missing */
- common->wcmChannel[channel].work.proximity = 1;
+ if (event->type == EV_KEY)
+ {
+ switch(event->code)
+ {
+ case BTN_TOUCH:
+ case BTN_TOOL_FINGER:
+ return 1;
+ }
+ }
+ else if (event->type == EV_ABS)
+ {
+ if (private->wcmDeviceType == TOUCH_ID)
+ {
+ /* filter ST for MT */
+ switch(event->code)
+ {
+ case ABS_X:
+ case ABS_Y:
+ case ABS_PRESSURE:
+ return 1;
+ }
+ }
+ else
+ {
+ /* filter MT for pen */
+ switch(event->code)
+ {
+ case ABS_MT_SLOT:
+ case ABS_MT_TRACKING_ID:
+ case ABS_MT_POSITION_X:
+ case ABS_MT_POSITION_Y:
+ case ABS_MT_PRESSURE:
+ return 1;
+ }
+ }
+ }
+ }
+
+ /* For generic devices, filter out doubletap/tripletap that
+ * can be confused with older protocol.
+ */
+ if (common->wcmProtocolLevel == WCM_PROTOCOL_GENERIC)
+ {
+ if (event->type == EV_KEY)
+ {
+ switch(event->code)
+ {
+ case BTN_TOOL_DOUBLETAP:
+ case BTN_TOOL_TRIPLETAP:
+ return 1;
+ }
+ }
}
- /* dispatch event */
- usbParseChannel(pInfo,channel);
+ return 0;
+}
-skipEvent:
- common->wcmLastToolSerial = 0;
- common->wcmEventCnt = 0;
+#define ERASER_BIT 0x008
+#define PUCK_BITS 0xf00
+#define PUCK_EXCEPTION 0x806
+/**
+ * Decide the tool type by its id for protocol 5 devices
+ *
+ * @param id The tool id received from the kernel.
+ * @return The tool type associated with the tool id.
+ */
+static int usbIdToType(int id)
+{
+ int type = STYLUS_ID;
+
+ /* The existing tool ids have the following patten: all pucks, except
+ * one, have the third byte set to zero; all erasers have the fourth
+ * bit set. The rest are styli.
+ */
+ if (id & ERASER_BIT)
+ type = ERASER_ID;
+ else if (!(id & PUCK_BITS) || (id == PUCK_EXCEPTION))
+ type = CURSOR_ID;
+
+ return type;
+}
+
+/**
+ * Find the tool type (STYLUS_ID, etc.) based on the device_id or the
+ * current tool serial number if the device_id is unknown (0).
+ *
+ * Protocol 5 devices report different IDs for different styli and pucks,
+ * Protocol 4 devices simply report STYLUS_DEVICE_ID, etc.
+ *
+ * @param ds The current device state received from the kernel.
+ * @return The tool type associated with the tool id or the current
+ * tool serial number.
+ */
+static int usbFindDeviceType(const WacomCommonPtr common,
+ const WacomDeviceState *ds)
+{
+ WacomToolPtr tool = NULL;
+ int device_type = 0;
+
+ if (!ds->device_id && ds->serial_num)
+ {
+ for (tool = common->wcmTool; tool; tool = tool->next)
+ if (ds->serial_num == tool->serial)
+ {
+ device_type = tool->typeid;
+ break;
+ }
+ }
+
+ if (device_type || !ds->device_id) return device_type;
+
+ switch (ds->device_id)
+ {
+ case STYLUS_DEVICE_ID:
+ device_type = STYLUS_ID;
+ break;
+ case ERASER_DEVICE_ID:
+ device_type = ERASER_ID;
+ break;
+ case CURSOR_DEVICE_ID:
+ device_type = CURSOR_ID;
+ break;
+ case TOUCH_DEVICE_ID:
+ device_type = TOUCH_ID;
+ break;
+ case PAD_DEVICE_ID:
+ device_type = PAD_ID;
+ break;
+ default: /* protocol 5 */
+ device_type = usbIdToType(ds->device_id);
+ }
+
+ return device_type;
+}
+
+static int usbParseAbsEvent(WacomCommonPtr common,
+ struct input_event *event, WacomDeviceState *ds)
+{
+ int change = 1;
+
+ switch(event->code)
+ {
+ case ABS_X:
+ ds->x = event->value;
+ break;
+ case ABS_Y:
+ ds->y = event->value;
+ break;
+ case ABS_RX:
+ ds->stripx = event->value;
+ break;
+ case ABS_RY:
+ ds->stripy = event->value;
+ break;
+ case ABS_RZ:
+ ds->rotation = event->value;
+ break;
+ case ABS_TILT_X:
+ ds->tiltx = event->value - common->wcmMaxtiltX/2;
+ break;
+ case ABS_TILT_Y:
+ ds->tilty = event->value - common->wcmMaxtiltY/2;
+ break;
+ case ABS_PRESSURE:
+ ds->pressure = event->value;
+ break;
+ case ABS_DISTANCE:
+ ds->distance = event->value;
+ break;
+ case ABS_WHEEL:
+ ds->abswheel = event->value;
+ break;
+ case ABS_Z:
+ ds->abswheel = event->value;
+ break;
+ case ABS_THROTTLE:
+ /* 2nd touch ring comes in over ABS_THROTTLE for 24HD */
+ if (common->vendor_id == WACOM_VENDOR_ID && common->tablet_id == 0xF4)
+ ds->abswheel2 = event->value;
+ else
+ ds->throttle = event->value;
+ break;
+ case ABS_MISC:
+ ds->proximity = (event->value != 0);
+ if (event->value)
+ {
+ ds->device_id = event->value;
+ ds->device_type = usbFindDeviceType(common, ds);
+ }
+ break;
+ default:
+ change = 0;
+ }
+ return change;
+}
+
+/**
+ * Flip the mask bit in buttons corresponding to btn to the specified state.
+ *
+ * @param buttons The current button mask
+ * @param btn Zero-indexed button number to change
+ * @param state Zero to unset, non-zero to set the mask for the button
+ *
+ * @return The new button mask
+ */
+static int mod_buttons(int buttons, int btn, int state)
+{
+ int mask;
+
+ if (btn >= sizeof(int) * 8)
+ {
+ xf86Msg(X_ERROR, "%s: Invalid button number %d. Insufficient "
+ "storage\n", __func__, btn);
+ return buttons;
+ }
+
+ mask = 1 << btn;
+
+ if (state)
+ buttons |= mask;
+ else
+ buttons &= ~mask;
+
+ return buttons;
+}
+
+static int usbParseAbsMTEvent(WacomCommonPtr common, struct input_event *event)
+{
+ int change = 1;
+ wcmUSBData* private = common->private;
+ WacomDeviceState *ds;
+
+ ds = &common->wcmChannel[private->wcmMTChannel].work;
+
+ switch(event->code)
+ {
+ case ABS_MT_SLOT:
+ if (event->value >= 0 && event->value < MAX_FINGERS)
+ private->wcmMTChannel = event->value;
+ break;
+
+ case ABS_MT_TRACKING_ID:
+ ds->proximity = (event->value != -1);
+ ds->device_type = TOUCH_ID;
+ ds->device_id = TOUCH_DEVICE_ID;
+ ds->serial_num = private->wcmMTChannel+1;
+ ds->sample = (int)GetTimeInMillis();
+ break;
+
+ case ABS_MT_POSITION_X:
+ ds->x = event->value;
+ break;
+
+ case ABS_MT_POSITION_Y:
+ ds->y = event->value;
+ break;
+
+ case ABS_MT_PRESSURE:
+ ds->pressure = event->value;
+ break;
+
+ default:
+ change = 0;
+ }
+ return change;
+}
+
+static struct
+{
+ unsigned long device_type;
+ unsigned long tool_key;
+} wcmTypeToKey [] =
+{
+ { STYLUS_ID, BTN_TOOL_PEN },
+ { STYLUS_ID, BTN_TOOL_PENCIL },
+ { STYLUS_ID, BTN_TOOL_BRUSH },
+ { STYLUS_ID, BTN_TOOL_AIRBRUSH },
+ { ERASER_ID, BTN_TOOL_RUBBER },
+ { CURSOR_ID, BTN_TOOL_MOUSE },
+ { CURSOR_ID, BTN_TOOL_LENS },
+ { TOUCH_ID, BTN_TOOL_DOUBLETAP },
+ { TOUCH_ID, BTN_TOOL_TRIPLETAP },
+ { PAD_ID, BTN_FORWARD },
+ { PAD_ID, BTN_0 }
+};
+
+static int usbParseKeyEvent(WacomCommonPtr common,
+ struct input_event *event, WacomDeviceState *ds,
+ WacomDeviceState *dslast)
+{
+ int change = 1;
+
+ /* BTN_TOOL_* are sent to indicate when a specific tool is going
+ * in our out of proximity. When going in proximity, here we
+ * initialize tool specific values. Making sure shared values
+ * are correct values during tool change is done elsewhere.
+ */
+ switch (event->code)
+ {
+ case BTN_TOOL_PEN:
+ case BTN_TOOL_PENCIL:
+ case BTN_TOOL_BRUSH:
+ case BTN_TOOL_AIRBRUSH:
+ ds->device_type = STYLUS_ID;
+ /* V5 tools use ABS_MISC to report device_id */
+ if (common->wcmProtocolLevel == WCM_PROTOCOL_4)
+ ds->device_id = STYLUS_DEVICE_ID;
+ ds->proximity = (event->value != 0);
+ DBG(6, common,
+ "USB stylus detected %x\n",
+ event->code);
+ break;
+
+ case BTN_TOOL_RUBBER:
+ ds->device_type = ERASER_ID;
+ /* V5 tools use ABS_MISC to report device_id */
+ if (common->wcmProtocolLevel == WCM_PROTOCOL_4)
+ ds->device_id = ERASER_DEVICE_ID;
+ ds->proximity = (event->value != 0);
+ if (ds->proximity)
+ ds->proximity = ERASER_PROX;
+ DBG(6, common,
+ "USB eraser detected %x (value=%d)\n",
+ event->code, event->value);
+ break;
+
+ case BTN_TOOL_MOUSE:
+ case BTN_TOOL_LENS:
+ DBG(6, common,
+ "USB mouse detected %x (value=%d)\n",
+ event->code, event->value);
+ ds->device_type = CURSOR_ID;
+ /* V5 tools use ABS_MISC to report device_id */
+ if (common->wcmProtocolLevel == WCM_PROTOCOL_4)
+ ds->device_id = CURSOR_DEVICE_ID;
+ ds->proximity = (event->value != 0);
+ break;
+
+ case BTN_TOUCH:
+ if (common->wcmProtocolLevel == WCM_PROTOCOL_GENERIC)
+ {
+ /* 1FG USB touchscreen */
+ if (!TabletHasFeature(common, WCM_PEN) &&
+ TabletHasFeature(common, WCM_1FGT) &&
+ TabletHasFeature(common, WCM_LCD))
+ {
+ DBG(6, common,
+ "USB 1FG Touch detected %x (value=%d)\n",
+ event->code, event->value);
+ ds->device_type = TOUCH_ID;
+ ds->device_id = TOUCH_DEVICE_ID;
+ ds->proximity = event->value;
+ }
+ }
+ break;
+
+ case BTN_TOOL_FINGER:
+ /* A pad tool */
+ if (common->wcmProtocolLevel != WCM_PROTOCOL_GENERIC)
+ {
+ DBG(6, common,
+ "USB Pad detected %x (value=%d)\n",
+ event->code, event->value);
+ ds->device_type = PAD_ID;
+ ds->device_id = PAD_DEVICE_ID;
+ ds->proximity = (event->value != 0);
+ break;
+ }
+
+ /* fall through */
+ case BTN_TOOL_DOUBLETAP:
+ DBG(6, common,
+ "USB Touch detected %x (value=%d)\n",
+ event->code, event->value);
+ ds->device_type = TOUCH_ID;
+ ds->device_id = TOUCH_DEVICE_ID;
+ ds->proximity = event->value;
+ /* time stamp for 2FGT gesture events */
+ if ((ds->proximity && !dslast->proximity) ||
+ (!ds->proximity && dslast->proximity))
+ ds->sample = (int)GetTimeInMillis();
+ break;
+
+ case BTN_TOOL_TRIPLETAP:
+ DBG(6, common,
+ "USB Touch second finger detected %x (value=%d)\n",
+ event->code, event->value);
+ ds->device_type = TOUCH_ID;
+ ds->device_id = TOUCH_DEVICE_ID;
+ ds->proximity = event->value;
+ /* time stamp for 2GT gesture events */
+ if ((ds->proximity && !dslast->proximity) ||
+ (!ds->proximity && dslast->proximity))
+ ds->sample = (int)GetTimeInMillis();
+ /* Second finger events will be considered in
+ * combination with the first finger data */
+ break;
+
+ default:
+ change = 0;
+ }
+
+ if (change)
+ return change;
+
+ /* Rest back to non-default value for next switch statement */
+ change = 1;
+
+ /* From this point on, all BTN_* will be real button presses.
+ * Stylus buttons always go with *ds. Handle remaining
+ * cases upon return.
+ */
+ switch (event->code)
+ {
+ case BTN_STYLUS:
+ ds->buttons = mod_buttons(ds->buttons, 1, event->value);
+ break;
+
+ case BTN_STYLUS2:
+ ds->buttons = mod_buttons(ds->buttons, 2, event->value);
+ break;
+
+ default:
+ change = 0;
+ }
+
+ return change;
+}
+
+/* Handle all button presses except for stylus buttons */
+static int usbParseBTNEvent(WacomCommonPtr common,
+ struct input_event *event, WacomDeviceState *ds)
+{
+ int nkeys;
+ int change = 1;
+
+ switch (event->code)
+ {
+ case BTN_LEFT:
+ ds->buttons = mod_buttons(ds->buttons, 0, event->value);
+ break;
+
+ case BTN_MIDDLE:
+ ds->buttons = mod_buttons(ds->buttons, 1, event->value);
+ break;
+
+ case BTN_RIGHT:
+ ds->buttons = mod_buttons(ds->buttons, 2, event->value);
+ break;
+
+ case BTN_SIDE:
+ case BTN_BACK:
+ ds->buttons = mod_buttons(ds->buttons, 3, event->value);
+ break;
+
+ case BTN_EXTRA:
+ case BTN_FORWARD:
+ ds->buttons = mod_buttons(ds->buttons, 4, event->value);
+ break;
+
+ default:
+ for (nkeys = 0; nkeys < common->npadkeys; nkeys++)
+ {
+ if (event->code == common->padkey_code[nkeys])
+ {
+ ds->buttons = mod_buttons(ds->buttons, nkeys, event->value);
+ break;
+ }
+ }
+ if (nkeys >= common->npadkeys)
+ change = 0;
+ }
+ return change;
}
-static void usbParseChannel(InputInfoPtr pInfo, int channel)
+/***
+ * Retrieve the tool type from an USB data packet by looking at the event
+ * codes. Refer to linux/input.h for event codes that define tool types.
+ *
+ * @param event_ptr A pointer to the USB data packet that contains the
+ * events to be processed.
+ * @param nevents Number of events in the packet.
+ * @param last_device_type The device type for the last event
+ *
+ * @return The tool type. last_device_type if no pen/touch/eraser event code
+ * in the event, or TOUCH_ID if last_device_type is not a tool.
+ */
+static int usbInitToolType(const struct input_event *event_ptr, int nevents, int last_device_type)
{
- int i, shift, nkeys;
- WacomDeviceState* ds;
+ int i, device_type = 0;
+ struct input_event* event = (struct input_event *)event_ptr;
+
+ for (i = 0; (i < nevents) && !device_type; ++i)
+ {
+ switch (event->code)
+ {
+ case BTN_TOOL_PEN:
+ case BTN_TOOL_PENCIL:
+ case BTN_TOOL_BRUSH:
+ case BTN_TOOL_AIRBRUSH:
+ device_type = STYLUS_ID;
+ break;
+
+ case BTN_TOOL_FINGER:
+ case ABS_MT_SLOT:
+ case ABS_MT_TRACKING_ID:
+ device_type = TOUCH_ID;
+ break;
+
+ case BTN_TOOL_RUBBER:
+ device_type = ERASER_ID;
+ break;
+ }
+
+ event++;
+ }
+
+ if (!device_type)
+ {
+ if (last_device_type)
+ device_type = last_device_type;
+ else
+ device_type = TOUCH_ID;
+ }
+
+ return device_type;
+}
+
+/**
+ * Check if the tool is a stylus/eraser and in-prox or not.
+ *
+ * @param device_type The tool type stored in wcmChannel
+ * @param proximity The tool's proximity state
+
+ * @return True if stylus/eraser is in-prox; False otherwise.
+ */
+static Bool usbIsPenInProx(int device_type, int proximity)
+{
+ Bool is_pen = (device_type == STYLUS_ID) ||
+ (device_type == ERASER_ID);
+ return (is_pen && proximity);
+}
+
+static void usbDispatchEvents(InputInfoPtr pInfo)
+{
+ int i;
+ WacomDeviceState *ds, *btn_ds;
struct input_event* event;
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
+ int channel;
+ int channel_change = 0, btn_channel_change = 0, mt_channel_change = 0;
+ WacomDeviceState dslast = common->wcmChannel[0].valid.state;
+ wcmUSBData* private = common->private;
- DBG(6, common, "%d events received\n", common->wcmEventCnt);
- #define MOD_BUTTONS(bit, value) do { \
- shift = 1<<bit; \
- ds->buttons = (((value) != 0) ? \
- (ds->buttons | (shift)) : (ds->buttons & ~(shift))); \
- } while (0)
+ DBG(6, common, "%d events received\n", private->wcmEventCnt);
- if (common->wcmEventCnt == 1 && !common->wcmEvents->type) {
- DBG(6, common, "no real events received\n");
+ if (private->wcmUseMT)
+ private->wcmDeviceType = usbInitToolType(private->wcmEvents,
+ private->wcmEventCnt,
+ dslast.device_type);
+
+ if (private->wcmPenTouch)
+ {
+ /* We get both pen and touch data from the kernel when they
+ * both are in/down. So, if we were (hence the need of dslast)
+ * processing pen events, we should ignore touch events.
+ *
+ * MT events will be posted to the userland when XInput 2.1
+ * is ready.
+ */
+ if ((private->wcmDeviceType == TOUCH_ID) &&
+ usbIsPenInProx(dslast.device_type, dslast.proximity))
+ {
+ private->wcmEventCnt = 0;
+ return;
+ }
+ }
+
+ channel = usbChooseChannel(common);
+
+ /* couldn't decide channel? invalid data */
+ if (channel == -1) {
+ private->wcmEventCnt = 0;
return;
}
- DBG(6, common, "%d events received\n", common->wcmEventCnt);
- /* all USB data operates from previous context except relative values*/
+ /* Protocol 5 devices have some complications related to DUALINPUT
+ * support and can not use below logic to recover from input
+ * event filtering. Instead, just live with occasional dropped
+ * event. Since tools are dynamically assigned a channel #, the
+ * structure must be initialized to known starting values
+ * when first entering proximity to discard invalid data.
+ */
+ if (common->wcmProtocolLevel == WCM_PROTOCOL_5)
+ {
+ if (!common->wcmChannel[channel].work.proximity)
+ memset(&common->wcmChannel[channel],0,
+ sizeof(WacomChannel));
+ }
+ else
+ {
+ /* Because of linux input filtering, each switch to a new
+ * tool is required to have its initial values match values
+ * of previous tool.
+ *
+ * For normal case, all tools are in channel 0 and so
+ * no issue. Protocol 4 2FGT devices split between
+ * two channels though and so need to copy data between
+ * channels to prevent loss of events; which could
+ * lead to cursor jumps.
+ *
+ * PAD device is special. It shares no events
+ * with other channels and is always in proximity.
+ * So it requires no copying of data from other
+ * channels.
+ */
+ if (private->wcmPrevChannel != channel &&
+ channel != PAD_CHANNEL &&
+ private->wcmPrevChannel != PAD_CHANNEL)
+ {
+ common->wcmChannel[channel].work =
+ common->wcmChannel[private->wcmPrevChannel].work;
+ private->wcmPrevChannel = channel;
+ }
+ }
+
ds = &common->wcmChannel[channel].work;
+ dslast = common->wcmChannel[channel].valid.state;
+
+ /* all USB data operates from previous context except relative values*/
ds->relwheel = 0;
- ds->serial_num = common->wcmLastToolSerial;
+ ds->serial_num = private->wcmLastToolSerial;
+
+ /* For protocol 4 and 5 devices, ds == btn_ds. */
+ btn_ds = &common->wcmChannel[private->wcmBTNChannel].work;
/* loop through all events in group */
- for (i=0; i<common->wcmEventCnt; ++i)
+ for (i=0; i<private->wcmEventCnt; ++i)
{
- event = common->wcmEvents + i;
+ event = private->wcmEvents + i;
DBG(11, common,
"event[%d]->type=%d code=%d value=%d\n",
i, event->type, event->code, event->value);
+ /* Check for events to be ignored and skip them up front. */
+ if (usbFilterEvent(common, event))
+ continue;
+
/* absolute events */
if (event->type == EV_ABS)
{
- if (event->code == ABS_X)
- ds->x = event->value;
- else if (event->code == ABS_Y)
- ds->y = event->value;
- else if (event->code == ABS_RX)
- ds->stripx = event->value;
- else if (event->code == ABS_RY)
- ds->stripy = event->value;
- else if (event->code == ABS_RZ)
- ds->rotation = event->value;
- else if (event->code == ABS_TILT_X)
- ds->tiltx = event->value - common->wcmMaxtiltX/2;
- else if (event->code == ABS_TILT_Y)
- ds->tilty = event->value - common->wcmMaxtiltY/2;
- else if (event->code == ABS_PRESSURE) {
- if (ds->device_type == TOUCH_ID)
- ds->capacity = event->value;
- else
- ds->pressure = event->value;
- } else if (event->code == ABS_DISTANCE)
- ds->distance = event->value;
- else if (event->code == ABS_WHEEL ||
- event->code == ABS_Z)
- ds->abswheel = event->value;
- else if (event->code == ABS_THROTTLE)
- ds->throttle = event->value;
- else if (event->code == ABS_MISC && event->value)
- ds->device_id = event->value;
+ if (usbParseAbsEvent(common, event, ds))
+ channel_change |= 1;
+ else if (usbParseAbsMTEvent(common, event))
+ {
+ if (private->wcmMTChannel == 0)
+ channel_change |= 1;
+ else if (private->wcmMTChannel == 1)
+ mt_channel_change |= 1;
+ }
}
else if (event->type == EV_REL)
{
if (event->code == REL_WHEEL)
+ {
ds->relwheel = -event->value;
+ channel_change |= 1;
+ }
else
xf86Msg(X_ERROR, "%s: rel event recv'd (%d)!\n",
pInfo->name, event->code);
}
-
else if (event->type == EV_KEY)
{
- if ((event->code == BTN_TOOL_PEN) ||
- (event->code == BTN_TOOL_PENCIL) ||
- (event->code == BTN_TOOL_BRUSH) ||
- (event->code == BTN_TOOL_AIRBRUSH))
- {
- ds->device_type = STYLUS_ID;
- /* V5 tools use ABS_MISC to report device_id */
- if (common->wcmProtocolLevel == 4)
- ds->device_id = STYLUS_DEVICE_ID;
- ds->proximity = (event->value != 0);
- DBG(6, common,
- "USB stylus detected %x\n",
- event->code);
- }
- else if (event->code == BTN_TOOL_RUBBER)
- {
- ds->device_type = ERASER_ID;
- /* V5 tools use ABS_MISC to report device_id */
- if (common->wcmProtocolLevel == 4)
- ds->device_id = ERASER_DEVICE_ID;
- ds->proximity = (event->value != 0);
- if (ds->proximity)
- ds->proximity = ERASER_PROX;
- DBG(6, common,
- "USB eraser detected %x (value=%d)\n",
- event->code, event->value);
- }
- else if ((event->code == BTN_TOOL_MOUSE) ||
- (event->code == BTN_TOOL_LENS))
- {
- DBG(6, common,
- "USB mouse detected %x (value=%d)\n",
- event->code, event->value);
- ds->device_type = CURSOR_ID;
- /* V5 tools use ABS_MISC to report device_id */
- if (common->wcmProtocolLevel == 4)
- ds->device_id = CURSOR_DEVICE_ID;
- ds->proximity = (event->value != 0);
- }
- else if (event->code == BTN_TOOL_FINGER)
- {
- DBG(6, common,
- "USB Pad detected %x (value=%d)\n",
- event->code, event->value);
- ds->device_type = PAD_ID;
- ds->device_id = PAD_DEVICE_ID;
- ds->proximity = (event->value != 0);
- }
- else if (event->code == BTN_TOOL_DOUBLETAP)
- {
- WacomChannelPtr pChannel = common->wcmChannel + channel;
- WacomDeviceState dslast = pChannel->valid.state;
- DBG(6, common,
- "USB Touch detected %x (value=%d)\n",
- event->code, event->value);
- ds->device_type = TOUCH_ID;
- ds->device_id = TOUCH_DEVICE_ID;
- ds->proximity = event->value;
- /* time stamp for 2FGT gesture events */
- if ((ds->proximity && !dslast.proximity) ||
- (!ds->proximity && dslast.proximity))
- ds->sample = (int)GetTimeInMillis();
- /* left button is always pressed for
- * touchscreen without capacity
- * when the first finger touch event received.
- * For touchscreen with capacity, left button
- * event will be decided
- * in wcmCommon.c by capacity threshold.
- * Touchpads should not have button
- * press.
- */
- if (common->wcmCapacityDefault < 0 &&
- (common->tablet_id < 0xd0 ||
- common->tablet_id > 0xd3))
- MOD_BUTTONS (0, event->value);
- }
- else if (event->code == BTN_TOOL_TRIPLETAP)
- {
- WacomChannelPtr pChannel = common->wcmChannel + channel;
- WacomDeviceState dslast = pChannel->valid.state;
- DBG(6, common,
- "USB Touch second finger detected %x (value=%d)\n",
- event->code, event->value);
- ds->device_type = TOUCH_ID;
- ds->device_id = TOUCH_DEVICE_ID;
- ds->proximity = event->value;
- /* time stamp for 2GT gesture events */
- if ((ds->proximity && !dslast.proximity) ||
- (!ds->proximity && dslast.proximity))
- ds->sample = (int)GetTimeInMillis();
- /* Second finger events will be considered in
- * combination with the first finger data */
- }
- else if ((event->code == BTN_STYLUS) ||
- (event->code == BTN_MIDDLE))
- {
- MOD_BUTTONS (1, event->value);
- }
- else if ((event->code == BTN_STYLUS2) ||
- (event->code == BTN_RIGHT))
- {
- MOD_BUTTONS (2, event->value);
- }
- else if (event->code == BTN_LEFT)
- MOD_BUTTONS (0, event->value);
- else if (event->code == BTN_SIDE)
- MOD_BUTTONS (3, event->value);
- else if (event->code == BTN_EXTRA)
- MOD_BUTTONS (4, event->value);
+ if (usbParseKeyEvent(common, event, ds, &dslast))
+ channel_change |= 1;
else
- {
- for (nkeys = 0; nkeys < common->npadkeys; nkeys++)
- if (event->code == common->padkey_code [nkeys])
- {
- MOD_BUTTONS (nkeys, event->value);
- break;
- }
- }
+ btn_channel_change |=
+ usbParseBTNEvent(common, event,
+ btn_ds);
}
} /* next event */
- /* don't send touch event when touch isn't enabled */
- if ((ds->device_type == TOUCH_ID) && !common->wcmTouch)
- return;
+ /* device type unknown? Tool may be on the tablet when X starts. */
+ if (!ds->device_type && !dslast.proximity)
+ {
+ unsigned long keys[NBITS(KEY_MAX)] = { 0 };
+
+ /* Retrieve the type by asking a resend from the kernel */
+ ioctl(common->fd, EVIOCGKEY(sizeof(keys)), keys);
+
+ for (i=0; i < ARRAY_SIZE(wcmTypeToKey); i++)
+ {
+ if (ISBITSET(keys, wcmTypeToKey[i].tool_key))
+ {
+ ds->device_type = wcmTypeToKey[i].device_type;
+ ds->proximity = 1;
+ break;
+ }
+ }
+ }
/* DTF720 and DTF720a don't support eraser */
if (((common->tablet_id == 0xC0) || (common->tablet_id == 0xC2)) &&
@@ -1058,8 +1580,64 @@ static void usbParseChannel(InputInfoPtr pInfo, int channel)
return;
}
- /* dispatch event */
- wcmEvent(common, channel, ds);
+ /*reset the serial number when the tool is going out */
+ if (!ds->proximity)
+ private->wcmLastToolSerial = 0;
+
+ /* don't send touch event when touch isn't enabled */
+ if (ds->device_type != TOUCH_ID || common->wcmTouch)
+ {
+ /* dispatch events */
+ if (channel_change ||
+ (private->wcmBTNChannel == channel && btn_channel_change))
+ wcmEvent(common, channel, ds);
+
+ /* dispatch for second finger.
+ * first finger is handled above. */
+ if (mt_channel_change)
+ {
+ WacomDeviceState *mt_ds;
+
+ mt_ds = &common->wcmChannel[1].work;
+ wcmEvent(common, 1, mt_ds);
+ }
+ }
+
+ /* dispatch butten events when re-routed */
+ if (private->wcmBTNChannel != channel && btn_channel_change)
+ wcmEvent(common, private->wcmBTNChannel, btn_ds);
+}
+
+/* Quirks to unify the tool and tablet types for GENERIC protocol tablet PCs
+ *
+ * @param[in,out] keys Contains keys queried from hardware. If a
+ * touchscreen is detected, keys are modified to add BTN_TOOL_FINGER so
+ * that a TOUCH device is created later.
+ * @param[in] abs Used to detect multi-touch touchscreens. When detected,
+ * updates keys to add possibly missing BTN_TOOL_DOUBLETAP.
+ * @param[in,out] common Used only for tablet features. Adds TCM_TPC for
+ * touchscreens so correct defaults, such as absolute mode, are used.
+ */
+static void usbGenericTouchscreenQuirks(unsigned long *keys,
+ unsigned long *abs,
+ WacomCommonPtr common)
+{
+ /* USB Tablet PC single finger touch devices do not emit
+ * BTN_TOOL_FINGER since it is a touchscreen device.
+ */
+ if (ISBITSET(keys, BTN_TOUCH) &&
+ !ISBITSET(keys, BTN_TOOL_FINGER) &&
+ !ISBITSET(keys, BTN_TOOL_PEN))
+ {
+ SETBIT(keys, BTN_TOOL_FINGER); /* 1FGT */
+ TabletSetFeature(common, WCM_TPC);
+ }
+
+ /* Serial Tablet PC two finger touch devices do not emit
+ * BTN_TOOL_DOUBLETAP since they are not touchpads.
+ */
+ if (ISBITSET(abs, ABS_MT_SLOT) && !ISBITSET(keys, BTN_TOOL_DOUBLETAP))
+ SETBIT(keys, BTN_TOOL_DOUBLETAP); /* 2FGT */
}
/**
@@ -1067,29 +1645,50 @@ static void usbParseChannel(InputInfoPtr pInfo, int channel)
* on success or 0 on failure.
* For USB devices, we simply copy the information the kernel gives us.
*/
-int usbProbeKeys(InputInfoPtr pInfo)
+static int usbProbeKeys(InputInfoPtr pInfo)
{
struct input_id wacom_id;
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
+ unsigned long abs[NBITS(ABS_MAX)] = {0};
if (ioctl(pInfo->fd, EVIOCGBIT(EV_KEY, (sizeof(unsigned long)
* NBITS(KEY_MAX))), common->wcmKeys) < 0)
{
- xf86Msg(X_ERROR, "%s: wcmDeviceTypeKeys unable to "
+ xf86Msg(X_ERROR, "%s: usbProbeKeys unable to "
"ioctl USB key bits.\n", pInfo->name);
return 0;
}
if (ioctl(pInfo->fd, EVIOCGID, &wacom_id) < 0)
{
- xf86Msg(X_ERROR, "%s: wcmDeviceTypeKeys unable to "
+ xf86Msg(X_ERROR, "%s: usbProbeKeys unable to "
"ioctl Device ID.\n", pInfo->name);
return 0;
}
+ if (ioctl(pInfo->fd, EVIOCGBIT(EV_ABS, sizeof(abs)), abs) < 0)
+ {
+ xf86Msg(X_ERROR, "%s: usbProbeKeys unable to ioctl "
+ "abs bits.\n", pInfo->name);
+ return 0;
+ }
+
+ /* The wcmKeys stored above have different meaning for generic
+ * protocol. Detect that and change default protocol 4 to
+ * generic.
+ */
+ if (!ISBITSET(abs, ABS_MISC))
+ {
+ common->wcmProtocolLevel = WCM_PROTOCOL_GENERIC;
+ usbGenericTouchscreenQuirks(common->wcmKeys, abs, common);
+ }
+
+ common->vendor_id = wacom_id.vendor;
+ common->tablet_id = wacom_id.product;
+
return wacom_id.product;
}
-/* vim: set noexpandtab shiftwidth=8: */
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/wcmValidateDevice.c b/src/wcmValidateDevice.c
index dec9a24..862e005 100644
--- a/src/wcmValidateDevice.c
+++ b/src/wcmValidateDevice.c
@@ -26,9 +26,6 @@
#include <fcntl.h>
#include <unistd.h>
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
-
-
/* wcmCheckSource - Check if there is another source defined this device
* before or not: don't add the tool by hal/udev if user has defined at least
* one tool for the device in xorg.conf. One device can have multiple tools
@@ -90,7 +87,7 @@ int wcmIsDuplicate(char* device, InputInfoPtr pInfo)
/* always allow xorg.conf defined tools to be added */
if (!strlen(lsource)) goto ret;
- if (fstat(pInfo->fd, &st) == -1)
+ if (stat(device, &st) == -1)
{
/* can not access major/minor to check device duplication */
xf86Msg(X_ERROR, "%s: stat failed (%s). cannot check for duplicates.\n",
@@ -123,36 +120,63 @@ ret:
static struct
{
const char* type;
- __u16 tool;
+ __u16 tool[3]; /* tool array is terminated by 0 */
} wcmType [] =
{
- { "stylus", BTN_TOOL_PEN },
- { "eraser", BTN_TOOL_RUBBER },
- { "cursor", BTN_TOOL_MOUSE },
- { "touch", BTN_TOOL_DOUBLETAP },
- { "pad", BTN_TOOL_FINGER }
+ { "stylus", { BTN_TOOL_PEN, 0 } },
+ { "eraser", { BTN_TOOL_RUBBER, 0 } },
+ { "cursor", { BTN_TOOL_MOUSE, 0 } },
+ { "touch", { BTN_TOOL_DOUBLETAP, BTN_TOOL_FINGER, 0 } },
+ { "pad", { BTN_FORWARD, BTN_0, 0 } }
};
/* validate tool type for device/product */
Bool wcmIsAValidType(InputInfoPtr pInfo, const char* type)
{
- int j, ret = FALSE;
+ int j, k, ret = FALSE;
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
+ char* dsource = xf86CheckStrOption(pInfo->options, "_source", "");
if (!type)
+ {
+ xf86Msg(X_ERROR, "%s: No type specified\n", pInfo->name);
return FALSE;
+ }
/* walkthrough all types */
for (j = 0; j < ARRAY_SIZE(wcmType); j++)
{
if (!strcmp(wcmType[j].type, type))
- if (ISBITSET (common->wcmKeys, wcmType[j].tool))
+ {
+ for (k = 0; wcmType[j].tool[k] && !ret; k++)
{
- ret = TRUE;
- break;
+ if (ISBITSET (common->wcmKeys, wcmType[j].tool[k]))
+ {
+ ret = TRUE;
+
+ /* non GENERIC devices use BTN_TOOL_FINGER for pad */
+ if (common->wcmProtocolLevel != WCM_PROTOCOL_GENERIC)
+ {
+ if (!strcmp(type, "touch") &&
+ wcmType[j].tool[k] == BTN_TOOL_FINGER)
+ ret = FALSE;
+ }
+ }
+ else if (!strlen(dsource)) /* an user defined type */
+ {
+ /* assume it is a valid type */
+ SETBIT(common->wcmKeys, wcmType[j].tool[k]);
+ ret = TRUE;
+ }
}
+ }
}
+
+ if (!ret)
+ xf86Msg(X_ERROR, "%s: Invalid type '%s' for this device.\n",
+ pInfo->name, type);
+
return ret;
}
@@ -161,24 +185,179 @@ int wcmDeviceTypeKeys(InputInfoPtr pInfo)
{
int ret = 1;
WacomDevicePtr priv = pInfo->private;
+ WacomCommonPtr common = priv->common;
+
+ priv->common->tablet_id = common->wcmDevCls->ProbeKeys(pInfo);
+
+ switch (priv->common->tablet_id)
+ {
+ case 0xF4: /* Cintiq 24HD */
+ TabletSetFeature(priv->common, WCM_DUALRING);
+ /* fall through */
+
+ case 0xB8: /* I4 */
+ case 0xB9: /* I4 */
+ case 0xBA: /* I4 */
+ case 0xBB: /* I4 */
+ case 0xBC: /* I4 */
+ case 0xBD: /* I4 */
+ TabletSetFeature(priv->common, WCM_ROTATION);
+ /* fall through */
+
+ /* tablets with touch ring */
+ case 0x17: /* BambooFun */
+ case 0x18: /* BambooFun */
+ TabletSetFeature(priv->common, WCM_RING);
+ break;
+
+ /* tablets support dual input */
+ case 0x20: /* I1 */
+ case 0x21: /* I1 */
+ case 0x22: /* I1 */
+ case 0x23: /* I1 */
+ case 0x24: /* I1 */
+ case 0x41: /* I2 */
+ case 0x42: /* I2 */
+ case 0x43: /* I2 */
+ case 0x44: /* I2 */
+ case 0x45: /* I2 */
+ case 0x47: /* I2 */
+ TabletSetFeature(priv->common, WCM_DUALINPUT);
+ break;
+
+ /* P4 display tablets */
+ case 0x30: /* PL400 */
+ case 0x31: /* PL500 */
+ case 0x32: /* PL600 */
+ case 0x33: /* PL600SX */
+ case 0x34: /* PL550 */
+ case 0x35: /* PL800 */
+ case 0x37: /* PL700 */
+ case 0x38: /* PL510 */
+ case 0x39: /* PL710 */
+ case 0xC0: /* DTF720 */
+ case 0xC2: /* DTF720a */
+ case 0xC4: /* DTF521 */
+ case 0xC7: /* DTU1931 */
+ case 0xCE: /* DTU2231 */
+ case 0xF0: /* DTU1631 */
+ TabletSetFeature(priv->common, WCM_LCD);
+ break;
+
+ /* tablets support menu strips */
+ case 0x3F: /* CintiqV5 */
+ case 0xC5: /* CintiqV5 */
+ case 0xC6: /* CintiqV5 */
+ case 0xCC: /* CinitqV5 */
+ TabletSetFeature(priv->common, WCM_LCD);
+ /* fall through */
+ case 0xB0: /* I3 */
+ case 0xB1: /* I3 */
+ case 0xB2: /* I3 */
+ case 0xB3: /* I3 */
+ case 0xB4: /* I3 */
+ case 0xB5: /* I3 */
+ case 0xB7: /* I3 */
+ TabletSetFeature(priv->common, WCM_STRIP | WCM_ROTATION);
+ break;
+
+ case 0xE2: /* TPC with 2FGT */
+ case 0xE3: /* TPC with 2FGT */
+ case 0xE6: /* TPC with 2FGT */
+ TabletSetFeature(priv->common, WCM_TPC);
+ break;
+
+ case 0x93: /* TPC with 1FGT */
+ case 0x9A: /* TPC with 1FGT */
+ case 0x90: /* TPC */
+ case 0x97: /* TPC */
+ TabletSetFeature(priv->common, WCM_TPC);
+ break;
- /* serial ISDV4 devices */
- priv->common->tablet_id = isdv4ProbeKeys(pInfo);
- if (!priv->common->tablet_id) /* USB devices */
- priv->common->tablet_id = usbProbeKeys(pInfo);
+ case 0x9F:
+ TabletSetFeature(priv->common, WCM_LCD);
+ break;
+ }
+
+ if (ISBITSET(common->wcmKeys, BTN_TOOL_PEN))
+ TabletSetFeature(priv->common, WCM_PEN);
+
+ if (ISBITSET (common->wcmKeys, BTN_0) ||
+ ISBITSET (common->wcmKeys, BTN_FORWARD))
+ {
+ TabletSetFeature(priv->common, WCM_PAD);
+ }
+
+ /* This handles both protocol 4 and 5 meanings of wcmKeys */
+ if (common->wcmProtocolLevel == WCM_PROTOCOL_4)
+ {
+ /* TRIPLETAP means 2 finger touch */
+ /* DOUBLETAP without TRIPLETAP means 1 finger touch */
+ if (ISBITSET(common->wcmKeys, BTN_TOOL_TRIPLETAP))
+ TabletSetFeature(priv->common, WCM_2FGT);
+ else if (ISBITSET(common->wcmKeys, BTN_TOOL_DOUBLETAP))
+ TabletSetFeature(priv->common, WCM_1FGT);
+ }
+
+ if (common->wcmProtocolLevel == WCM_PROTOCOL_GENERIC)
+ {
+ /* DOUBLETAP means 2 finger touch */
+ /* FINGER without DOUBLETAP means 1 finger touch */
+ if (ISBITSET(common->wcmKeys, BTN_TOOL_DOUBLETAP))
+ TabletSetFeature(priv->common, WCM_2FGT);
+ else if (ISBITSET(common->wcmKeys, BTN_TOOL_FINGER))
+ TabletSetFeature(priv->common, WCM_1FGT);
+ }
return ret;
}
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 14
+static InputOption*
+input_option_new(InputOption *list, char *key, char *value)
+{
+ InputOption *new;
+
+ new = calloc(1, sizeof(InputOption));
+ new->key = key;
+ new->value = value;
+ new->next = list;
+ return new;
+}
+
+static void
+input_option_free_list(InputOption **opts)
+{
+ InputOption *tmp = *opts;
+ while(*opts)
+ {
+ tmp = (*opts)->next;
+ free((*opts)->key);
+ free((*opts)->value);
+ free((*opts));
+ *opts = tmp;
+ }
+}
+#endif
+
/**
* Duplicate xf86 options, replace the "type" option with the given type
- * (and the name with "$name $type" and convert them to InputOption */
-static InputOption *wcmOptionDupConvert(InputInfoPtr pInfo, const char *type)
+ * (and the name with "$name $type" and convert them to InputOption
+ *
+ * @param basename Kernel device name for this device
+ * @param type Tool type (cursor, eraser, etc.)
+ * @param serial Serial number this device should be bound to (-1 for "any")
+ */
+static InputOption *wcmOptionDupConvert(InputInfoPtr pInfo, const char* basename, const char *type, int serial)
{
+ WacomDevicePtr priv = pInfo->private;
+ WacomCommonPtr common = priv->common;
pointer original = pInfo->options;
- InputOption *iopts = NULL, *new;
+ WacomToolPtr ser = common->serials;
+ InputOption *iopts = NULL;
char *name;
pointer options;
+ int rc;
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
options = xf86OptionListDuplicate(original);
@@ -191,68 +370,173 @@ static InputOption *wcmOptionDupConvert(InputInfoPtr pInfo, const char *type)
options = dummy.options;
}
#endif
+ if (serial > -1)
+ {
+ while (ser->serial && ser->serial != serial)
+ ser = ser->next;
- name = calloc(strlen(pInfo->name) + strlen(type) + 2, 1);
- sprintf(name, "%s %s", pInfo->name, type);
+ if (strlen(ser->name) > 0)
+ rc = asprintf(&name, "%s %s %s", basename, ser->name, type);
+ else
+ rc = asprintf(&name, "%s %d %s", basename, ser->serial, type);
+ }
+ else
+ rc = asprintf(&name, "%s %s", basename, type);
+
+ if (rc == -1) /* if asprintf fails, strdup will probably too... */
+ name = strdup("unknown");
options = xf86ReplaceStrOption(options, "Type", type);
options = xf86ReplaceStrOption(options, "Name", name);
+
+ if (serial > -1)
+ options = xf86ReplaceIntOption(options, "Serial", ser->serial);
+
free(name);
while(options)
{
- new = calloc(1, sizeof(InputOption));
-
- new->key = xf86OptionName(options);
- new->value = xf86OptionValue(options);
- new->next = iopts;
- iopts = new;
+ iopts = input_option_new(iopts,
+ xf86OptionName(options),
+ xf86OptionValue(options));
options = xf86NextOption(options);
}
return iopts;
}
-static void wcmFreeInputOpts(InputOption* opts)
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+/**
+ * Duplicate the attributes of the given device. "product" gets the type
+ * appended, so a device of product "Wacom" will then have a product "Wacom
+ * eraser", "Wacom cursor", etc.
+ */
+static InputAttributes* wcmDuplicateAttributes(InputInfoPtr pInfo,
+ const char *type)
{
- InputOption *tmp = opts;
- while(opts)
- {
- tmp = opts->next;
- free(opts->key);
- free(opts->value);
- free(opts);
- opts = tmp;
- }
+ int rc;
+ InputAttributes *attr;
+ attr = DuplicateInputAttributes(pInfo->attrs);
+ rc = asprintf(&attr->product, "%s %s", attr->product, type);
+ if (rc == -1)
+ attr->product = NULL;
+ return attr;
}
+#endif
+
+/**
+ * This struct contains the necessary info for hotplugging a device later.
+ * Memory must be freed after use.
+ */
+typedef struct {
+ InputOption *input_options;
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 9
+ InputAttributes *attrs;
+#endif
+} WacomHotplugInfo;
/**
- * Hotplug one device of the given type.
+ * Actually hotplug the device. This function is called by the server when
+ * the WorkProcs are processed.
+ *
+ * @param client The server client. unused
+ * @param closure A pointer to a struct WcmHotplugInfo containing the
+ * necessary information to create a new device.
+ * @return TRUE to remove this function from the server's work queue.
+ */
+static Bool
+wcmHotplugDevice(ClientPtr client, pointer closure )
+{
+ WacomHotplugInfo *hotplug_info = closure;
+ DeviceIntPtr dev; /* dummy */
+
+ NewInputDeviceRequest(hotplug_info->input_options,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 9
+ hotplug_info->attrs,
+#endif
+ &dev);
+ input_option_free_list(&hotplug_info->input_options);
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+ FreeInputAttributes(hotplug_info->attrs);
+#endif
+ free(hotplug_info);
+
+ return TRUE;
+}
+
+/**
+ * Queue the hotplug for one tool/device of the given type.
* Device has the same options as the "parent" device, type is one of
* erasor, stylus, pad, touch, cursor, etc.
* Name of the new device is set automatically to "<device name> <type>".
+ *
+ * Note that we don't actually hotplug the device here. We store the
+ * information needed to hotplug the device later and then queue the
+ * hotplug. The server will come back and call the @wcmHotplugDevice
+ * later.
+ *
+ * @param pInfo The parent device
+ * @param basename The base name for the device (type will be appended)
+ * @param type Type name for this tool
+ * @param serial Serial number this device should be bound to (-1 for "any")
*/
-static void wcmHotplug(InputInfoPtr pInfo, const char *type)
+static void wcmQueueHotplug(InputInfoPtr pInfo, const char* basename, const char *type, int serial)
{
- DeviceIntPtr dev; /* dummy */
- InputOption *input_options;
+ WacomHotplugInfo *hotplug_info;
- input_options = wcmOptionDupConvert(pInfo, type);
+ hotplug_info = calloc(1, sizeof(WacomHotplugInfo));
- NewInputDeviceRequest(input_options,
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 9
- NULL,
+ if (!hotplug_info)
+ {
+ xf86Msg(X_ERROR, "%s: OOM, cannot hotplug dependent devices\n", pInfo->name);
+ return;
+ }
+
+ hotplug_info->input_options = wcmOptionDupConvert(pInfo, basename, type, serial);
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+ hotplug_info->attrs = wcmDuplicateAttributes(pInfo, type);
#endif
- &dev);
- wcmFreeInputOpts(input_options);
+ QueueWorkProc(wcmHotplugDevice, serverClient, hotplug_info);
}
-void wcmHotplugOthers(InputInfoPtr pInfo)
+/**
+ * Hotplug all serial numbers configured on this device.
+ *
+ * @param basename The kernel device name
+ */
+static void wcmHotplugSerials(InputInfoPtr pInfo, const char *basename)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+ WacomToolPtr ser = common->serials;
+
+ while (ser)
+ {
+ xf86Msg(X_INFO, "%s: hotplugging serial %d.\n", pInfo->name, ser->serial);
+
+ if (wcmIsAValidType(pInfo, "stylus") &&
+ (ser->typeid & STYLUS_ID))
+ wcmQueueHotplug(pInfo, basename, "stylus", ser->serial);
+
+ if (wcmIsAValidType(pInfo, "eraser") &&
+ (ser->typeid & ERASER_ID))
+ wcmQueueHotplug(pInfo, basename, "eraser", ser->serial);
+
+ if (wcmIsAValidType(pInfo, "cursor") &&
+ (ser->typeid & CURSOR_ID))
+ wcmQueueHotplug(pInfo, basename, "cursor", ser->serial);
+
+ ser = ser->next;
+ }
+}
+
+void wcmHotplugOthers(InputInfoPtr pInfo, const char *basename)
{
int i, skip = 1;
- char* device;
- xf86Msg(X_INFO, "%s: hotplugging dependent devices.\n", pInfo->name);
- device = xf86SetStrOption(pInfo->options, "Device", NULL);
+ xf86Msg(X_INFO, "%s: hotplugging dependent devices.\n", pInfo->name);
+
/* same loop is used to init the first device, if we get here we
* need to start at the second one */
for (i = 0; i < ARRAY_SIZE(wcmType); i++)
@@ -262,9 +546,12 @@ void wcmHotplugOthers(InputInfoPtr pInfo)
if (skip)
skip = 0;
else
- wcmHotplug(pInfo, wcmType[i].type);
+ wcmQueueHotplug(pInfo, basename, wcmType[i].type, -1);
}
}
+
+ wcmHotplugSerials(pInfo, basename);
+
xf86Msg(X_INFO, "%s: hotplugging completed.\n", pInfo->name);
}
@@ -305,32 +592,125 @@ int wcmNeedAutoHotplug(InputInfoPtr pInfo, const char **type)
xf86Msg(X_INFO, "%s: type not specified, assuming '%s'.\n", pInfo->name, *type);
xf86Msg(X_INFO, "%s: other types will be automatically added.\n", pInfo->name);
+ /* Note: wcmIsHotpluggedDevice() relies on this */
pInfo->options = xf86AddNewOption(pInfo->options, "Type", *type);
pInfo->options = xf86ReplaceStrOption(pInfo->options, "_source", "_driver/wacom");
return 1;
}
-int wcmParseOptions(InputInfoPtr pInfo)
+int wcmParseSerials (InputInfoPtr pInfo)
+{
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
+ char *s;
+
+ if (common->serials)
+ {
+ return 0; /*Parse has been already done*/
+ }
+
+ s = xf86SetStrOption(pInfo->options, "ToolSerials", NULL);
+ if (s) /*Dont parse again, if the commons have values already*/
+ {
+ char* tok = strtok(s, ";");
+ while (tok != NULL)
+ {
+ int serial, nmatch;
+ char type[strlen(tok) + 1];
+ char name[strlen(tok) + 1];
+ WacomToolPtr ser = calloc(1, sizeof(WacomTool));
+
+ if (ser == NULL)
+ return 1;
+
+ nmatch = sscanf(tok,"%d,%[a-z],%[A-Za-z ]",&serial, type, name);
+
+ if (nmatch < 1)
+ {
+ xf86Msg(X_ERROR, "%s: %s is invalid serial string.\n",
+ pInfo->name, tok);
+ return 1;
+ }
+
+ if (nmatch >= 1)
+ {
+ xf86Msg(X_CONFIG, "%s: Tool serial %d found.\n",
+ pInfo->name, serial);
+
+ ser->serial = serial;
+
+ ser->typeid = STYLUS_ID | ERASER_ID; /*Default to both tools*/
+ }
+
+ if (nmatch >= 2)
+ {
+ xf86Msg(X_CONFIG, "%s: Tool %d has type %s.\n",
+ pInfo->name, serial, type);
+ if ((strcmp(type, "pen") == 0) || (strcmp(type, "airbrush") == 0))
+ ser->typeid = STYLUS_ID | ERASER_ID;
+ else if (strcmp(type, "artpen") == 0)
+ ser->typeid = STYLUS_ID;
+ else if (strcmp(type, "cursor") == 0)
+ ser->typeid = CURSOR_ID;
+ else xf86Msg(X_CONFIG, "%s: Invalid type %s, defaulting to pen.\n",
+ pInfo->name, type);
+ }
+
+ if (nmatch == 3)
+ {
+ xf86Msg(X_CONFIG, "%s: Tool %d is named %s.\n",
+ pInfo->name, serial, name);
+ ser->name = strdup(name);
+ }
+ else ser->name = ""; /*no name yet*/
+
+ if (common->serials == NULL)
+ common->serials = ser;
+ else
+ {
+ WacomToolPtr tool = common->serials;
+ while (tool->next)
+ tool = tool->next;
+ tool->next = ser;
+ }
+
+ tok = strtok(NULL,";");
+ }
+ }
+ return 0;
+}
+
+/**
+ * Parse the pre-init options for this device. Most useful for options
+ * needed to properly init a device (baud rate for example).
+ *
+ * Note that parameters is_primary and is_dependent are mutually exclusive,
+ * though both may be false in the case of an xorg.conf device.
+ *
+ * @param is_primary True if the device is the parent device for
+ * hotplugging, False if the device is a depent or xorg.conf device.
+ * @param is_hotplugged True if the device is a dependent device, FALSE
+ * otherwise.
+ * @retvalue True on success or False otherwise.
+ */
+Bool wcmPreInitParseOptions(InputInfoPtr pInfo, Bool is_primary,
+ Bool is_dependent)
{
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
char *s, b[12];
- int i, oldButton;
+ int i;
WacomToolPtr tool = NULL;
- WacomToolAreaPtr area = NULL;
+ int tpc_button_is_on;
/* Optional configuration */
- priv->debugLevel = xf86SetIntOption(pInfo->options,
- "DebugLevel", priv->debugLevel);
- common->debugLevel = xf86SetIntOption(pInfo->options,
- "CommonDBG", common->debugLevel);
s = xf86SetStrOption(pInfo->options, "Mode", NULL);
if (s && (xf86NameCmp(s, "absolute") == 0))
- priv->flags |= ABSOLUTE_FLAG;
+ set_absolute(pInfo, TRUE);
else if (s && (xf86NameCmp(s, "relative") == 0))
- priv->flags &= ~ABSOLUTE_FLAG;
+ set_absolute(pInfo, FALSE);
else
{
if (s)
@@ -342,38 +722,51 @@ int wcmParseOptions(InputInfoPtr pInfo)
*/
}
- /* Pad is always in relative mode.
+ /* Pad is always in absolute mode.
* The pad also defaults to wheel scrolling, unlike the pens
* (interesting effects happen on ArtPen and others with build-in
* wheels)
*/
if (IsPad(priv))
{
- priv->flags &= ~ABSOLUTE_FLAG;
- priv->wheelup = 4;
- priv->wheeldn = 5;
+ priv->wheelup = priv->wheel2up = 4;
+ priv->wheeldn = priv->wheel2dn = 5;
+ set_absolute(pInfo, TRUE);
}
- /* Store original local Core flag so it can be changed later */
- if (pInfo->flags & (XI86_ALWAYS_CORE))
- priv->flags |= COREEVENT_FLAG;
-
s = xf86SetStrOption(pInfo->options, "Rotate", NULL);
if (s)
{
+ int rotation = ROTATE_NONE;
+
if (xf86NameCmp(s, "CW") == 0)
- common->wcmRotate=ROTATE_CW;
+ rotation = ROTATE_CW;
else if (xf86NameCmp(s, "CCW") ==0)
- common->wcmRotate=ROTATE_CCW;
+ rotation = ROTATE_CCW;
else if (xf86NameCmp(s, "HALF") ==0)
- common->wcmRotate=ROTATE_HALF;
+ rotation = ROTATE_HALF;
else if (xf86NameCmp(s, "NONE") !=0)
{
xf86Msg(X_ERROR, "%s: invalid Rotate option '%s'.\n",
pInfo->name, s);
goto error;
}
+
+ if (is_dependent && rotation != common->wcmRotate)
+ xf86Msg(X_INFO, "%s: ignoring rotation of dependent"
+ " device\n", pInfo->name);
+ else
+ wcmRotateTablet(pInfo, rotation);
+ }
+
+ common->wcmRawSample = xf86SetIntOption(pInfo->options, "RawSample",
+ common->wcmRawSample);
+ if (common->wcmRawSample < 1 || common->wcmRawSample > MAX_SAMPLES)
+ {
+ xf86Msg(X_ERROR, "%s: RawSample setting '%d' out of range [1..%d]. Using default.\n",
+ pInfo->name, common->wcmRawSample, MAX_SAMPLES);
+ common->wcmRawSample = DEFAULT_SAMPLES;
}
common->wcmSuppress = xf86SetIntOption(pInfo->options, "Suppress",
@@ -390,18 +783,14 @@ int wcmParseOptions(InputInfoPtr pInfo)
(common->wcmFlags & TILT_REQUEST_FLAG)))
common->wcmFlags |= TILT_REQUEST_FLAG;
- if (xf86SetBoolOption(pInfo->options, "RawFilter",
- (common->wcmFlags & RAW_FILTERING_FLAG)))
- common->wcmFlags |= RAW_FILTERING_FLAG;
-
/* pressure curve takes control points x1,y1,x2,y2
* values in range from 0..100.
* Linear curve is 0,0,100,100
* Slightly depressed curve might be 5,0,100,95
* Slightly raised curve might be 0,5,95,100
*/
- s = xf86SetStrOption(pInfo->options, "PressCurve", NULL);
- if (s && (IsStylus(priv) || IsEraser(priv)))
+ s = xf86SetStrOption(pInfo->options, "PressCurve", "0,0,100,100");
+ if (s && (IsPen(priv) || IsTouch(priv)))
{
int a,b,c,d;
if ((sscanf(s,"%d,%d,%d,%d",&a,&b,&c,&d) != 4) ||
@@ -412,41 +801,19 @@ int wcmParseOptions(InputInfoPtr pInfo)
wcmSetPressureCurve(priv,a,b,c,d);
}
+ /*Serials of tools we want hotpluged*/
+ if (wcmParseSerials (pInfo) != 0)
+ goto error;
+
if (IsCursor(priv))
{
common->wcmCursorProxoutDist = xf86SetIntOption(pInfo->options, "CursorProx", 0);
- if (common->wcmCursorProxoutDist < 0 || common->wcmCursorProxoutDist > 255)
+ if (common->wcmCursorProxoutDist < 0 ||
+ common->wcmCursorProxoutDist > common->wcmMaxDist)
xf86Msg(X_CONFIG, "%s: CursorProx invalid %d \n",
pInfo->name, common->wcmCursorProxoutDist);
}
- /* Configure Monitors' resoluiton in TwinView setup.
- * The value is in the form of "1024x768,1280x1024"
- * for a desktop of monitor 1 at 1024x768 and
- * monitor 2 at 1280x1024
- */
- s = xf86SetStrOption(pInfo->options, "TVResolution", NULL);
- if (s)
- {
- int a,b,c,d;
- if ((sscanf(s,"%dx%d,%dx%d",&a,&b,&c,&d) != 4) ||
- (a <= 0) || (b <= 0) || (c <= 0) || (d <= 0))
- xf86Msg(X_CONFIG, "%s: TVResolution not valid\n",
- pInfo->name);
- else
- {
- priv->tvResolution[0] = a;
- priv->tvResolution[1] = b;
- priv->tvResolution[2] = c;
- priv->tvResolution[3] = d;
- }
- }
-
- priv->screen_no = xf86SetIntOption(pInfo->options, "ScreenNo", -1);
-
- if (xf86SetBoolOption(pInfo->options, "KeepShape", 0))
- priv->flags |= KEEP_SHAPE_FLAG;
-
priv->topX = xf86SetIntOption(pInfo->options, "TopX", 0);
priv->topY = xf86SetIntOption(pInfo->options, "TopY", 0);
priv->bottomX = xf86SetIntOption(pInfo->options, "BottomX", 0);
@@ -454,11 +821,6 @@ int wcmParseOptions(InputInfoPtr pInfo)
priv->serial = xf86SetIntOption(pInfo->options, "Serial", 0);
tool = priv->tool;
- area = priv->toolarea;
- area->topX = priv->topX;
- area->topY = priv->topY;
- area->bottomX = priv->bottomX;
- area->bottomY = priv->bottomY;
tool->serial = priv->serial;
/* The first device doesn't need to add any tools/areas as it
@@ -474,18 +836,10 @@ int wcmParseOptions(InputInfoPtr pInfo)
if(toollist) /* Already have a tool with the same type/serial */
{
- WacomToolAreaPtr arealist;
-
- free(tool);
- priv->tool = tool = toollist;
- arealist = toollist->arealist;
-
- /* Add the area to the end of the list */
- while(arealist->next)
- arealist = arealist->next;
- arealist->next = area;
- }
- else /* No match on existing tool/serial, add tool to the end of the list */
+ xf86Msg(X_ERROR, "%s: already have a tool with type/serial %d/%d.",
+ pInfo->name, tool->typeid, tool->serial);
+ goto error;
+ } else /* No match on existing tool/serial, add tool to the end of the list */
{
toollist = common->wcmTool;
while(toollist->next)
@@ -494,126 +848,140 @@ int wcmParseOptions(InputInfoPtr pInfo)
}
}
- common->wcmScaling = 0;
-
common->wcmThreshold = xf86SetIntOption(pInfo->options, "Threshold",
common->wcmThreshold);
- common->wcmMaxZ = xf86SetIntOption(pInfo->options, "MaxZ",
- common->wcmMaxZ);
if (xf86SetBoolOption(pInfo->options, "ButtonsOnly", 0))
priv->flags |= BUTTONS_ONLY_FLAG;
- /* Tablet PC button applied to the whole tablet. Not just one tool */
- if ( priv->flags & STYLUS_ID )
- common->wcmTPCButton = xf86SetBoolOption(pInfo->options,
- "TPCButton",
- common->wcmTPCButtonDefault);
+ /* TPCButton on for Tablet PC by default */
+ tpc_button_is_on = xf86SetBoolOption(pInfo->options, "TPCButton",
+ TabletHasFeature(common, WCM_TPC));
- /* a single touch device */
- if (ISBITSET (common->wcmKeys, BTN_TOOL_DOUBLETAP))
+ if (is_primary || IsStylus(priv))
+ common->wcmTPCButton = tpc_button_is_on;
+ else if (tpc_button_is_on != common->wcmTPCButton)
+ xf86Msg(X_WARNING, "%s: TPCButton option can only be set "
+ "by stylus.\n", pInfo->name);
+
+ /* a single or double touch device */
+ if (TabletHasFeature(common, WCM_1FGT) ||
+ TabletHasFeature(common, WCM_2FGT))
{
+ int touch_is_on;
+
/* TouchDefault was off for all devices
* except when touch is supported */
common->wcmTouchDefault = 1;
+
+ touch_is_on = xf86SetBoolOption(pInfo->options, "Touch",
+ common->wcmTouchDefault);
+
+ if (is_primary || IsTouch(priv))
+ common->wcmTouch = touch_is_on;
+ else if (touch_is_on != common->wcmTouch)
+ xf86Msg(X_WARNING, "%s: Touch option can only be set "
+ "by a touch tool.\n", pInfo->name);
}
/* 2FG touch device */
- if (ISBITSET (common->wcmKeys, BTN_TOOL_TRIPLETAP))
+ if (TabletHasFeature(common, WCM_2FGT))
{
+ int gesture_is_on;
+
/* GestureDefault was off for all devices
* except when multi-touch is supported */
common->wcmGestureDefault = 1;
- }
- /* check if touch was turned off in xorg.conf */
- common->wcmTouch = xf86SetBoolOption(pInfo->options, "Touch",
- common->wcmTouchDefault);
+ gesture_is_on = xf86SetBoolOption(pInfo->options, "Gesture",
+ common->wcmGestureDefault);
- /* Touch gesture applies to the whole tablet */
- common->wcmGesture = xf86SetBoolOption(pInfo->options, "Gesture",
- common->wcmGestureDefault);
+ if (is_primary || IsTouch(priv))
+ common->wcmGesture = gesture_is_on;
+ else if (gesture_is_on != common->wcmGesture)
+ xf86Msg(X_WARNING, "%s: Touch gesture option can only "
+ "be set by a touch tool.\n", pInfo->name);
- /* Touch capacity applies to the whole tablet */
- common->wcmCapacity = xf86SetBoolOption(pInfo->options, "Capacity", common->wcmCapacityDefault);
+ common->wcmGestureParameters.wcmTapTime =
+ xf86SetIntOption(pInfo->options, "TapTime",
+ common->wcmGestureParameters.wcmTapTime);
+ }
- /* Mouse cursor stays in one monitor in a multimonitor setup */
- if ( !priv->wcmMMonitor )
- priv->wcmMMonitor = xf86SetBoolOption(pInfo->options, "MMonitor", 1);
+ /* Swap stylus buttons 2 and 3 for Tablet PCs */
+ if (TabletHasFeature(common, WCM_TPC) && IsStylus(priv))
+ {
+ priv->button[1] = 3;
+ priv->button[2] = 2;
+ }
for (i=0; i<WCM_MAX_BUTTONS; i++)
{
sprintf(b, "Button%d", i+1);
- s = xf86SetStrOption(pInfo->options, b, NULL);
- if (s)
- {
- oldButton = priv->button[i];
- priv->button[i] = xf86SetIntOption(pInfo->options, b, priv->button[i]);
- }
+ priv->button[i] = xf86SetIntOption(pInfo->options, b, priv->button[i]);
}
- if (common->wcmForceDevice == DEVICE_ISDV4)
- {
- int val;
- val = xf86SetIntOption(pInfo->options, "BaudRate", 38400);
+ /* Now parse class-specific options */
+ if (common->wcmDevCls->ParseOptions &&
+ !common->wcmDevCls->ParseOptions(pInfo))
+ goto error;
- switch(val)
- {
- case 38400:
- case 19200:
- common->wcmISDV4Speed = val;
- break;
- default:
- xf86Msg(X_ERROR, "%s: Illegal speed value "
- "(must be 19200 or 38400).",
- pInfo->name);
- break;
- }
- }
-
- s = xf86SetStrOption(pInfo->options, "Twinview", NULL);
- if (s && xf86NameCmp(s, "none") == 0)
- priv->twinview = TV_NONE;
- else if ((s && xf86NameCmp(s, "horizontal") == 0) ||
- (s && xf86NameCmp(s, "rightof") == 0))
- priv->twinview = TV_LEFT_RIGHT;
- else if ((s && xf86NameCmp(s, "vertical") == 0) ||
- (s && xf86NameCmp(s, "belowof") == 0))
- priv->twinview = TV_ABOVE_BELOW;
- else if (s && xf86NameCmp(s, "leftof") == 0)
- priv->twinview = TV_RIGHT_LEFT;
- else if (s && xf86NameCmp(s, "aboveof") == 0)
- priv->twinview = TV_BELOW_ABOVE;
- else if (s)
- {
- xf86Msg(X_ERROR, "%s: invalid Twinview (should be none, vertical (belowof), "
- "horizontal (rightof), aboveof, or leftof). Using none.\n",
- pInfo->name);
- priv->twinview = TV_NONE;
- }
-
- return 1;
+ return TRUE;
error:
- free(area);
free(tool);
- return 0;
+ return FALSE;
}
-int wcmAutoProbeDevice(InputInfoPtr pInfo)
+/* The values were based on trail and error. */
+#define WCM_BAMBOO3_MAXX 4096.0
+#define WCM_BAMBOO3_ZOOM_DISTANCE 180.0
+#define WCM_BAMBOO3_SCROLL_DISTANCE 80.0
+#define WCM_BAMBOO3_SCROLL_SPREAD_DISTANCE 350.0
+
+/**
+ * Parse post-init options for this device. Useful for overriding HW
+ * specific options computed during init phase (HW distances for example).
+ *
+ * Note that parameters is_primary and is_dependent are mutually exclusive,
+ * though both may be false in the case of an xorg.conf device.
+ *
+ * @param is_primary True if the device is the parent device for
+ * hotplugging, False if the device is a depent or xorg.conf device.
+ * @param is_hotplugged True if the device is a dependent device, FALSE
+ * otherwise.
+ * @retvalue True on success or False otherwise.
+ */
+Bool wcmPostInitParseOptions(InputInfoPtr pInfo, Bool is_primary,
+ Bool is_dependent)
{
- WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
- WacomCommonPtr common = priv->common;
+ WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
- if ((!common->wcmDevice || !strcmp (common->wcmDevice, "auto-dev")))
+ common->wcmMaxZ = xf86SetIntOption(pInfo->options, "MaxZ",
+ common->wcmMaxZ);
+
+ /* 2FG touch device */
+ if (TabletHasFeature(common, WCM_2FGT) && IsTouch(priv))
{
- common->wcmFlags |= AUTODEV_FLAG;
- if (! (common->wcmDevice = wcmEventAutoDevProbe (pInfo)))
- {
- xf86Msg(X_ERROR, "%s: unable to probe device\n",
- pInfo->name);
- return 0;
- }
+ int zoom_distance = common->wcmMaxTouchX *
+ (WCM_BAMBOO3_ZOOM_DISTANCE / WCM_BAMBOO3_MAXX);
+ int scroll_distance = common->wcmMaxTouchX *
+ (WCM_BAMBOO3_SCROLL_DISTANCE / WCM_BAMBOO3_MAXX);
+
+ common->wcmGestureParameters.wcmZoomDistance =
+ xf86SetIntOption(pInfo->options, "ZoomDistance",
+ zoom_distance);
+
+ common->wcmGestureParameters.wcmScrollDistance =
+ xf86SetIntOption(pInfo->options, "ScrollDistance",
+ scroll_distance);
+
+ common->wcmGestureParameters.wcmMaxScrollFingerSpread =
+ common->wcmMaxTouchX *
+ (WCM_BAMBOO3_SCROLL_SPREAD_DISTANCE / WCM_BAMBOO3_MAXX);
}
- return 1;
+
+
+ return TRUE;
}
-/* vim: set noexpandtab shiftwidth=8: */
+
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/wcmXCommand.c b/src/wcmXCommand.c
index 174e301..40393dc 100644
--- a/src/wcmXCommand.c
+++ b/src/wcmXCommand.c
@@ -24,6 +24,16 @@
#include "xf86Wacom.h"
#include "wcmFilter.h"
#include <exevents.h>
+#include <xf86_OSproc.h>
+
+#ifndef XI_PROP_DEVICE_NODE
+#define XI_PROP_DEVICE_NODE "Device Node"
+#endif
+#ifndef XI_PROP_PRODUCT_ID
+#define XI_PROP_PRODUCT_ID "Device Product ID"
+#endif
+
+static void wcmBindToSerial(InputInfoPtr pInfo, unsigned int serial);
/*****************************************************************************
* wcmDevSwitchModeCall --
@@ -32,26 +42,17 @@
int wcmDevSwitchModeCall(InputInfoPtr pInfo, int mode)
{
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
- int is_absolute = priv->flags & ABSOLUTE_FLAG;
DBG(3, priv, "to mode=%d\n", mode);
- /* Pad is always in relative mode.*/
+ /* Pad is always in absolute mode.*/
if (IsPad(priv))
- return (mode == Relative) ? Success : XI_BadMode;
+ return (mode == Absolute) ? Success : XI_BadMode;
- if ((mode == Absolute) && !is_absolute)
- {
- priv->flags |= ABSOLUTE_FLAG;
- wcmInitialCoordinates(pInfo, 0);
- wcmInitialCoordinates(pInfo, 1);
- }
- else if ((mode == Relative) && is_absolute)
- {
- priv->flags &= ~ABSOLUTE_FLAG;
- wcmInitialCoordinates(pInfo, 0);
- wcmInitialCoordinates(pInfo, 1);
- }
+ if ((mode == Absolute) && !is_absolute(pInfo))
+ set_absolute(pInfo, TRUE);
+ else if ((mode == Relative) && is_absolute(pInfo))
+ set_absolute(pInfo, FALSE);
else if ( (mode != Absolute) && (mode != Relative))
{
DBG(10, priv, "invalid mode=%d\n", mode);
@@ -78,53 +79,46 @@ int wcmDevSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode)
return wcmDevSwitchModeCall(pInfo, mode);
}
-/*****************************************************************************
- * wcmChangeScreen
- ****************************************************************************/
-
-void wcmChangeScreen(InputInfoPtr pInfo, int value)
-{
- WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
-
- if (priv->screen_no != value)
- {
- priv->screen_no = value;
- xf86ReplaceIntOption(pInfo->options, "ScreenNo", value);
- }
-
- if (priv->screen_no != -1)
- priv->currentScreen = priv->screen_no;
- wcmInitialScreens(pInfo);
- wcmInitialCoordinates(pInfo, 0);
- wcmInitialCoordinates(pInfo, 1);
-}
-
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
-
+Atom prop_devnode;
Atom prop_rotation;
Atom prop_tablet_area;
-Atom prop_screen_area;
Atom prop_pressurecurve;
Atom prop_serials;
+Atom prop_serial_binding;
Atom prop_strip_buttons;
Atom prop_wheel_buttons;
-Atom prop_display;
Atom prop_tv_resolutions;
-Atom prop_screen;
Atom prop_cursorprox;
-Atom prop_capacity;
Atom prop_threshold;
Atom prop_suppress;
Atom prop_touch;
+Atom prop_gesture;
+Atom prop_gesture_param;
Atom prop_hover;
Atom prop_tooltype;
Atom prop_btnactions;
+Atom prop_product_id;
#ifdef DEBUG
Atom prop_debuglevels;
#endif
-/* Special case: format -32 means type is XA_ATOM */
-static Atom InitWcmAtom(DeviceIntPtr dev, char *name, int format, int nvalues, int *values)
+/**
+ * Registers a property for the input device. This function registers
+ * the property name atom, as well as creates the property itself.
+ * At creation, the property values are initialized from the 'values'
+ * array. The device property is marked as non-deletable.
+ * Initialization values are always to be provided by means of an
+ * array of 32 bit integers, regardless of 'format'
+ *
+ * @param dev Pointer to device structure
+ * @param name Name of device property
+ * @param type Type of the property
+ * @param format Format of the property (8/16/32)
+ * @param nvalues Number of values in the property
+ * @param values Pointer to 32 bit integer array of initial property values
+ * @return Atom handle of property name
+ */
+static Atom InitWcmAtom(DeviceIntPtr dev, char *name, Atom type, int format, int nvalues, int *values)
{
int i;
Atom atom;
@@ -132,13 +126,6 @@ static Atom InitWcmAtom(DeviceIntPtr dev, char *name, int format, int nvalues, i
uint16_t val_16[WCM_MAX_MOUSE_BUTTONS];
uint32_t val_32[WCM_MAX_MOUSE_BUTTONS];
pointer converted = val_32;
- Atom type = XA_INTEGER;
-
- if (format == -32)
- {
- type = XA_ATOM;
- format = 32;
- }
for (i = 0; i < nvalues; i++)
{
@@ -173,129 +160,468 @@ void InitWcmDeviceProperties(InputInfoPtr pInfo)
DBG(10, priv, "\n");
- values[0] = priv->topX;
- values[1] = priv->topY;
- values[2] = priv->bottomX;
- values[3] = priv->bottomY;
- prop_tablet_area = InitWcmAtom(pInfo->dev, WACOM_PROP_TABLET_AREA, 32, 4, values);
+ prop_devnode = MakeAtom(XI_PROP_DEVICE_NODE, strlen(XI_PROP_DEVICE_NODE), TRUE);
+ XIChangeDeviceProperty(pInfo->dev, prop_devnode, XA_STRING, 8,
+ PropModeReplace, strlen(common->device_path),
+ common->device_path, FALSE);
+ XISetDevicePropertyDeletable(pInfo->dev, prop_devnode, FALSE);
+
+ if (!IsPad(priv)) {
+ values[0] = priv->topX;
+ values[1] = priv->topY;
+ values[2] = priv->bottomX;
+ values[3] = priv->bottomY;
+ prop_tablet_area = InitWcmAtom(pInfo->dev, WACOM_PROP_TABLET_AREA, XA_INTEGER, 32, 4, values);
+ }
values[0] = common->wcmRotate;
- prop_rotation = InitWcmAtom(pInfo->dev, WACOM_PROP_ROTATION, 8, 1, values);
+ prop_rotation = InitWcmAtom(pInfo->dev, WACOM_PROP_ROTATION, XA_INTEGER, 8, 1, values);
- if (IsStylus(priv) || IsEraser(priv)) {
+ if (IsPen(priv) || IsTouch(priv)) {
values[0] = priv->nPressCtrl[0];
values[1] = priv->nPressCtrl[1];
values[2] = priv->nPressCtrl[2];
values[3] = priv->nPressCtrl[3];
- prop_pressurecurve = InitWcmAtom(pInfo->dev, WACOM_PROP_PRESSURECURVE, 32, 4, values);
+ prop_pressurecurve = InitWcmAtom(pInfo->dev, WACOM_PROP_PRESSURECURVE, XA_INTEGER, 32, 4, values);
}
values[0] = common->tablet_id;
values[1] = priv->old_serial;
values[2] = priv->old_device_id;
- values[3] = priv->serial;
- prop_serials = InitWcmAtom(pInfo->dev, WACOM_PROP_SERIALIDS, 32, 4, values);
-
- if (IsPad(priv)) {
- values[0] = priv->striplup;
- values[1] = priv->stripldn;
- values[2] = priv->striprup;
- values[3] = priv->striprdn;
- prop_strip_buttons = InitWcmAtom(pInfo->dev, WACOM_PROP_STRIPBUTTONS, 8, 4, values);
-
- values[0] = priv->relup;
- values[1] = priv->reldn;
- values[2] = priv->wheelup;
- values[3] = priv->wheeldn;
- prop_wheel_buttons = InitWcmAtom(pInfo->dev, WACOM_PROP_WHEELBUTTONS, 8, 4, values);
- }
-
- values[0] = priv->tvResolution[0];
- values[1] = priv->tvResolution[1];
- values[2] = priv->tvResolution[2];
- values[3] = priv->tvResolution[3];
- prop_tv_resolutions = InitWcmAtom(pInfo->dev, WACOM_PROP_TWINVIEW_RES, 32, 4, values);
+ values[3] = priv->cur_serial;
+ prop_serials = InitWcmAtom(pInfo->dev, WACOM_PROP_SERIALIDS, XA_INTEGER, 32, 4, values);
+ values[0] = priv->serial;
+ prop_serial_binding = InitWcmAtom(pInfo->dev, WACOM_PROP_SERIAL_BIND, XA_INTEGER, 32, 1, values);
- values[0] = priv->screen_no;
- values[1] = priv->twinview;
- values[2] = priv->wcmMMonitor;
- prop_display = InitWcmAtom(pInfo->dev, WACOM_PROP_DISPLAY_OPTS, 8, 3, values);
-
- values[0] = priv->screenTopX[priv->currentScreen];
- values[1] = priv->screenTopY[priv->currentScreen];
- values[2] = priv->screenBottomX[priv->currentScreen];
- values[3] = priv->screenBottomY[priv->currentScreen];
- prop_screen = InitWcmAtom(pInfo->dev, WACOM_PROP_SCREENAREA, 32, 4, values);
-
- values[0] = common->wcmCursorProxoutDist;
- prop_cursorprox = InitWcmAtom(pInfo->dev, WACOM_PROP_PROXIMITY_THRESHOLD, 32, 1, values);
-
- values[0] = common->wcmCapacity;
- prop_capacity = InitWcmAtom(pInfo->dev, WACOM_PROP_CAPACITY, 32, 1, values);
+ if (IsCursor(priv)) {
+ values[0] = common->wcmCursorProxoutDist;
+ prop_cursorprox = InitWcmAtom(pInfo->dev, WACOM_PROP_PROXIMITY_THRESHOLD, XA_INTEGER, 32, 1, values);
+ }
values[0] = (!common->wcmMaxZ) ? 0 : common->wcmThreshold;
- prop_threshold = InitWcmAtom(pInfo->dev, WACOM_PROP_PRESSURE_THRESHOLD, 32, 1, values);
+ prop_threshold = InitWcmAtom(pInfo->dev, WACOM_PROP_PRESSURE_THRESHOLD, XA_INTEGER, 32, 1, values);
values[0] = common->wcmSuppress;
values[1] = common->wcmRawSample;
- prop_suppress = InitWcmAtom(pInfo->dev, WACOM_PROP_SAMPLE, 32, 2, values);
+ prop_suppress = InitWcmAtom(pInfo->dev, WACOM_PROP_SAMPLE, XA_INTEGER, 32, 2, values);
values[0] = common->wcmTouch;
- prop_touch = InitWcmAtom(pInfo->dev, WACOM_PROP_TOUCH, 8, 1, values);
+ prop_touch = InitWcmAtom(pInfo->dev, WACOM_PROP_TOUCH, XA_INTEGER, 8, 1, values);
+
+ if (IsStylus(priv)) {
+ values[0] = !common->wcmTPCButton;
+ prop_hover = InitWcmAtom(pInfo->dev, WACOM_PROP_HOVER, XA_INTEGER, 8, 1, values);
+ }
- values[0] = !common->wcmTPCButton;
- prop_hover = InitWcmAtom(pInfo->dev, WACOM_PROP_HOVER, 8, 1, values);
+ values[0] = common->wcmGesture;
+ prop_gesture = InitWcmAtom(pInfo->dev, WACOM_PROP_ENABLE_GESTURE, XA_INTEGER, 8, 1, values);
+ values[0] = common->wcmGestureParameters.wcmZoomDistance;
+ values[1] = common->wcmGestureParameters.wcmScrollDistance;
+ values[2] = common->wcmGestureParameters.wcmTapTime;
+ prop_gesture_param = InitWcmAtom(pInfo->dev, WACOM_PROP_GESTURE_PARAMETERS, XA_INTEGER, 32, 3, values);
values[0] = MakeAtom(pInfo->type_name, strlen(pInfo->type_name), TRUE);
- prop_tooltype = InitWcmAtom(pInfo->dev, WACOM_PROP_TOOL_TYPE, -32, 1, values);
+ prop_tooltype = InitWcmAtom(pInfo->dev, WACOM_PROP_TOOL_TYPE, XA_ATOM, 32, 1, values);
/* default to no actions */
memset(values, 0, sizeof(values));
- prop_btnactions = InitWcmAtom(pInfo->dev, WACOM_PROP_BUTTON_ACTIONS, -32, WCM_MAX_MOUSE_BUTTONS, values);
+ prop_btnactions = InitWcmAtom(pInfo->dev, WACOM_PROP_BUTTON_ACTIONS, XA_ATOM, 32, WCM_MAX_MOUSE_BUTTONS, values);
+
+ if (IsPad(priv)) {
+ memset(values, 0, sizeof(values));
+ prop_strip_buttons = InitWcmAtom(pInfo->dev, WACOM_PROP_STRIPBUTTONS, XA_ATOM, 32, 4, values);
+ }
+
+ if (IsPad(priv) || IsCursor(priv))
+ {
+ memset(values, 0, sizeof(values));
+ prop_wheel_buttons = InitWcmAtom(pInfo->dev, WACOM_PROP_WHEELBUTTONS, XA_ATOM, 32, 6, values);
+ }
+
+ values[0] = common->vendor_id;
+ values[1] = common->tablet_id;
+ prop_product_id = InitWcmAtom(pInfo->dev, XI_PROP_PRODUCT_ID, XA_INTEGER, 32, 2, values);
#ifdef DEBUG
values[0] = priv->debugLevel;
values[1] = common->debugLevel;
- prop_debuglevels = InitWcmAtom(pInfo->dev, WACOM_PROP_DEBUGLEVELS, 8, 2, values);
+ prop_debuglevels = InitWcmAtom(pInfo->dev, WACOM_PROP_DEBUGLEVELS, XA_INTEGER, 8, 2, values);
#endif
}
-struct workproc_data {
- DeviceIntPtr dev;
- Atom property;
- Atom type;
- short format;
- INT32 values[4];
-};
+/* Returns the offset of the property in the list given. If the property is
+ * not found, a negative error code is returned. */
+static int wcmFindProp(Atom property, Atom *prop_list, int nprops)
+{
+ int i;
+
+ /* check all properties used for button actions */
+ for (i = 0; i < nprops; i++)
+ if (prop_list[i] == property)
+ break;
+
+ if (i >= nprops)
+ return -BadAtom;
+
+ return i;
+}
+
+static int wcmSanityCheckProperty(XIPropertyValuePtr prop)
+{
+ CARD32 *data;
+ int j;
+
+ if (prop->size >= 255 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ data = (CARD32*)prop->data;
+
+ for (j = 0; j < prop->size; j++)
+ {
+ int code = data[j] & AC_CODE;
+ int type = data[j] & AC_TYPE;
+
+ switch(type)
+ {
+ case AC_KEY:
+ break;
+ case AC_BUTTON:
+ if (code > WCM_MAX_MOUSE_BUTTONS)
+ return BadValue;
+ break;
+ case AC_DISPLAYTOGGLE:
+ case AC_MODETOGGLE:
+ break;
+ default:
+ return BadValue;
+ }
+ }
+
+ return Success;
+}
/**
- * WorkProc called after resetting the tablet area.
+ * Store the new value of the property in one of the driver's internal
+ * property handler lists. Properties stored there will be checked for value
+ * changes whenever updated.
*/
-static Bool reset_area_property(ClientPtr client, pointer closure)
+static void wcmUpdateActionPropHandlers(XIPropertyValuePtr prop, Atom *handlers)
+{
+ int i;
+ CARD32 *values = (CARD32*)prop->data;
+
+ /* any action property needs to be registered for this handler. */
+ for (i = 0; i < prop->size; i++)
+ handlers[i] = values[i];
+}
+
+static void wcmUpdateButtonKeyActions(DeviceIntPtr dev, XIPropertyValuePtr prop,
+ unsigned int (*keys)[256], int skeys)
{
- struct workproc_data *data = closure;
- InputInfoPtr pInfo = xf86FirstLocalDevice();
- DeviceIntPtr dev = pInfo->dev;
+ Atom *values = (Atom*)prop->data;
+ XIPropertyValuePtr val;
+ int i, j;
+
+ for (i = 0; i < prop->size; i++)
+ {
+ /* keys is one based array to align with X buttons */
+ memset(keys[i+1], 0, sizeof(keys[i+1]));
+
+ if (!values[i])
+ continue;
+
+ XIGetDeviceProperty(dev, values[i], &val);
- for (; dev; dev = dev->next)
- if (dev == data->dev)
+ for (j = 0; j < val->size; j++)
+ keys[i+1][j] = ((unsigned int*)val->data)[j];
+ }
+}
+
+/* Change the properties that hold the actual button actions */
+static int wcmSetActionProperties(DeviceIntPtr dev, Atom property,
+ XIPropertyValuePtr prop, BOOL checkonly)
+{
+ InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
+ WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+ int i;
+ int rc;
+
+
+ DBG(10, priv, "\n");
+
+ rc = wcmSanityCheckProperty(prop);
+ if (rc != Success)
+ return rc;
+
+ i = wcmFindProp(property, priv->btn_actions, ARRAY_SIZE(priv->btn_actions));
+ if (i >= 0)
+ {
+ if (!checkonly)
+ {
+ XIGetDeviceProperty(dev, prop_btnactions, &prop);
+ wcmUpdateButtonKeyActions(dev, prop, priv->keys, ARRAY_SIZE(priv->keys));
+ }
+ } else
+ {
+ i = wcmFindProp(property, priv->wheel_actions,
+ ARRAY_SIZE(priv->wheel_actions));
+ if (i >= 0) {
+ if (!checkonly)
+ {
+ XIGetDeviceProperty(dev, prop_wheel_buttons, &prop);
+ wcmUpdateButtonKeyActions(dev, prop,
+ priv->wheel_keys,
+ ARRAY_SIZE(priv->wheel_keys));
+ }
+ } else
+ {
+ i = wcmFindProp(property, priv->strip_actions, ARRAY_SIZE(priv->strip_actions));
+ if (i >= 0 && !checkonly)
+ {
+ XIGetDeviceProperty(dev, prop_strip_buttons, &prop);
+ wcmUpdateButtonKeyActions(dev, prop, priv->strip_keys, ARRAY_SIZE(priv->strip_keys));
+ }
+ }
+ }
+
+ return abs(i);
+}
+
+static int wcmCheckActionProp(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop)
+{
+ InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
+ XIPropertyValuePtr val;
+ Atom *values = (Atom*)prop->data;
+ int i;
+
+ for (i = 0; i < prop->size; i++)
+ {
+ if (!values[i])
+ continue;
+
+ if (values[i] == property || !ValidAtom(values[i]))
+ return BadValue;
+
+ if (XIGetDeviceProperty(pInfo->dev, values[i], &val) != Success)
+ return BadValue;
+ }
+
+ return Success;
+}
+
+/* Change the property that refers to which properties the actual button
+ * actions are stored in */
+static int wcmSetPropertyButtonActions(DeviceIntPtr dev, Atom property,
+ XIPropertyValuePtr prop, BOOL checkonly)
+{
+ InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
+ WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+ int rc;
+
+ DBG(10, priv, "\n");
+
+ if (prop->format != 32 || prop->type != XA_ATOM)
+ return BadMatch;
+
+ /* How this works:
+ * prop_btnactions has a list of atoms stored. Any atom references
+ * another property on that device that contains the actual action.
+ * If this property changes, all action-properties are queried for
+ * their value and their value is stored in priv->key[button].
+ *
+ * If the button is pressed, the actions are executed.
+ *
+ * Any button action property needs to be monitored by this property
+ * handler too.
+ */
+
+ rc = wcmCheckActionProp(dev, property, prop);
+ if (rc != Success)
+ return rc;
+
+ if (!checkonly)
+ {
+ wcmUpdateActionPropHandlers(prop, priv->btn_actions);
+ wcmUpdateButtonKeyActions(dev, prop, priv->keys, ARRAY_SIZE(priv->keys));
+
+ }
+ return Success;
+}
+
+struct wheel_strip_update_t {
+ /* for CARD8 values, points to fields in struct to be updated */
+ int *up1;
+ int *dn1;
+ int *up2;
+ int *dn2;
+ int *up3;
+ int *dn3;
+
+ /* for CARD32 values, points to atom array of atoms to be
+ * monitored.*/
+ Atom *handlers;
+ /* for CARD32 values, points to key array that keeps the actual
+ actions.*/
+ int skeys; /* size of first keys dimensions */
+ unsigned int (*keys)[256];
+};
+
+static int wcmSetWheelOrStripProperty(DeviceIntPtr dev, Atom property,
+ XIPropertyValuePtr prop, BOOL checkonly,
+ struct wheel_strip_update_t *wsup)
+{
+ int rc;
+
+ union multival {
+ CARD8 *v8;
+ CARD32 *v32;
+ } values;
+
+ if ((property == prop_strip_buttons && prop->size != 4) ||
+ (property == prop_wheel_buttons && prop->size != 6))
+ return BadValue;
+
+ /* see wcmSetPropertyButtonActions for how this works. The wheel is
+ * slightly different in that it allows for 8 bit properties for
+ * pure buttons too */
+
+ values.v8 = (CARD8*)prop->data;
+
+ switch (prop->format)
+ {
+ case 8:
+ if (values.v8[0] > WCM_MAX_MOUSE_BUTTONS ||
+ values.v8[1] > WCM_MAX_MOUSE_BUTTONS ||
+ values.v8[2] > WCM_MAX_MOUSE_BUTTONS ||
+ values.v8[3] > WCM_MAX_MOUSE_BUTTONS ||
+ values.v8[4] > WCM_MAX_MOUSE_BUTTONS ||
+ values.v8[5] > WCM_MAX_MOUSE_BUTTONS)
+ return BadValue;
+
+ if (!checkonly) {
+ *wsup->up1 = values.v8[0];
+ *wsup->dn1 = values.v8[1];
+ *wsup->up2 = values.v8[2];
+ *wsup->dn2 = values.v8[3];
+ *wsup->up3 = values.v8[4];
+ *wsup->dn3 = values.v8[5];
+ }
break;
+ case 32:
+ rc = wcmCheckActionProp(dev, property, prop);
+ if (rc != Success)
+ return rc;
- /* device was removed between property update and calling the work proc */
- if (!dev)
- goto out;
+ if (!checkonly)
+ {
+ wcmUpdateActionPropHandlers(prop, wsup->handlers);
+ wcmUpdateButtonKeyActions(dev, prop, wsup->keys,
+ wsup->skeys);
+ }
- XIChangeDeviceProperty(dev, data->property, data->type, data->format,
- PropModeReplace, 4, data->values, TRUE);
+ break;
+ default:
+ return BadMatch;
+ }
-out:
- free(data);
- return TRUE;
+ return Success;
}
+static int wcmSetWheelProperty(DeviceIntPtr dev, Atom property,
+ XIPropertyValuePtr prop, BOOL checkonly)
+{
+ InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
+ WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+
+ struct wheel_strip_update_t wsup = {
+ .up1 = &priv->relup,
+ .dn1 = &priv->reldn,
+ .up2 = &priv->wheelup,
+ .dn2 = &priv->wheeldn,
+ .up3 = &priv->wheel2up,
+ .dn3 = &priv->wheel2dn,
+
+ .handlers = priv->wheel_actions,
+ .keys = priv->wheel_keys,
+ .skeys = 6,
+ };
+
+ return wcmSetWheelOrStripProperty(dev, property, prop, checkonly, &wsup);
+}
+
+static int wcmSetStripProperty(DeviceIntPtr dev, Atom property,
+ XIPropertyValuePtr prop, BOOL checkonly)
+{
+ InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
+ WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+
+ struct wheel_strip_update_t wsup = {
+ .up1 = &priv->striplup,
+ .dn1 = &priv->stripldn,
+ .up2 = &priv->striprup,
+ .dn2 = &priv->striprdn,
+ .up3 = NULL,
+ .dn3 = NULL,
+
+ .handlers = priv->strip_actions,
+ .keys = priv->strip_keys,
+ .skeys = 4,
+ };
+
+ return wcmSetWheelOrStripProperty(dev, property, prop, checkonly, &wsup);
+}
+
+/**
+ * Update the rotation property for all tools on the same physical tablet as
+ * pInfo.
+ */
+void wcmUpdateRotationProperty(WacomDevicePtr priv)
+{
+ WacomCommonPtr common = priv->common;
+ WacomDevicePtr other;
+ char rotation = common->wcmRotate;
+
+ for (other = common->wcmDevices; other; other = other->next)
+ {
+ InputInfoPtr pInfo;
+ DeviceIntPtr dev;
+
+ if (other == priv)
+ continue;
+
+ pInfo = other->pInfo;
+ dev = pInfo->dev;
+
+ XIChangeDeviceProperty(dev, prop_rotation, XA_INTEGER, 8,
+ PropModeReplace, 1, &rotation,
+ TRUE);
+ }
+}
+
+/**
+ * Only allow deletion of a property if it is not being used by any of the
+ * button actions.
+ */
+int wcmDeleteProperty(DeviceIntPtr dev, Atom property)
+{
+ InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
+ WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+ int i;
+
+ i = wcmFindProp(property, priv->btn_actions, ARRAY_SIZE(priv->btn_actions));
+ if (i < 0)
+ i = wcmFindProp(property, priv->wheel_actions,
+ ARRAY_SIZE(priv->wheel_actions));
+ if (i < 0)
+ i = wcmFindProp(property, priv->strip_actions,
+ ARRAY_SIZE(priv->strip_actions));
+
+ return (i >= 0) ? BadAccess : Success;
+}
+
int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
BOOL checkonly)
{
@@ -305,90 +631,30 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
DBG(10, priv, "\n");
- if (property == prop_tablet_area)
+ if (property == prop_devnode || property == prop_product_id)
+ return BadValue; /* Read-only */
+ else if (property == prop_tablet_area)
{
INT32 *values = (INT32*)prop->data;
- WacomToolAreaPtr area = priv->toolarea;
if (prop->size != 4 || prop->format != 32)
return BadValue;
- /* if set with -1/-1, we're re-updating ourselves with the
- * actual values (after setting them). Avoid loops by claiming success
- * if we're not actually updating anything */
- if (priv->topX == values[0] && priv->topY == values[1] &&
- priv->bottomX == values[2] && priv->bottomY == values[3])
- return Success;
-
- /* value validation is unnecessary since we let utility programs, such as
- * xsetwacom and userland control panel take care of the validation role.
- * when all four values are set to -1, it is an area reset (xydefault) */
- if ((values[0] != -1) || (values[1] != -1) ||
- (values[2] != -1) || (values[3] != -1))
- {
- WacomToolArea tmp_area = *area;
-
- area->topX = values[0];
- area->topY = values[1];
- area->bottomX = values[2];
- area->bottomY = values[3];
-
- /* validate the area */
- if (wcmAreaListOverlap(area, priv->tool->arealist))
- {
- *area = tmp_area;
- return BadValue;
- }
- *area = tmp_area;
- }
-
if (!checkonly)
{
- area->topX = values[0];
- area->topY = values[1];
- area->bottomX = values[2];
- area->bottomY = values[3];
-
if ((values[0] == -1) && (values[1] == -1) &&
(values[2] == -1) && (values[3] == -1))
{
- struct workproc_data *data;
- area->topX = 0;
- area->topY = 0;
- area->bottomX = priv->maxX;
- area->bottomY = priv->maxY;
-
- /* When updating with -1/-1, we need to
- * reupdate the property with the actual
- * values, otherwise it'll store -1/-1. We
- * can't do that directly though, we need to
- * schedule a work proc.
- */
- data = calloc(1, sizeof(struct workproc_data));
- if (data)
- {
- data->dev = dev;
- data->property = property;
- data->format = prop->format;
- data->type = prop->type;
- data->values[0] = area->topX;
- data->values[1] = area->topY;
- data->values[2] = area->bottomX;
- data->values[3] = area->bottomY;
- QueueWorkProc(reset_area_property, serverClient, data);
- }
+ values[0] = 0;
+ values[1] = 0;
+ values[2] = priv->maxX;
+ values[3] = priv->maxY;
}
-#if 0
- else /* offset for multimonitor */
- wcmAdjustArea(pInfo, area);
-#endif
- priv->topX = area->topX;
- priv->topY = area->topY;
- priv->bottomX = area->bottomX;
- priv->bottomY = area->bottomY;
- wcmInitialCoordinates(pInfo, 0);
- wcmInitialCoordinates(pInfo, 1);
+ priv->topX = values[0];
+ priv->topY = values[1];
+ priv->bottomX = values[2];
+ priv->bottomY = values[3];
}
} else if (property == prop_pressurecurve)
{
@@ -403,7 +669,7 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
pcurve[2], pcurve[3]))
return BadValue;
- if (IsCursor(priv) || IsPad (priv) || IsTouch (priv))
+ if (IsCursor(priv) || IsPad (priv))
return BadValue;
if (!checkonly)
@@ -418,10 +684,10 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
values = (CARD32*)prop->data;
- if ((values[0] < 0) || (values[0] > 100))
+ if (values[0] > 100)
return BadValue;
- if ((values[1] < 0) || (values[1] > XWACOM_MAX_SAMPLES))
+ if ((values[1] < 1) || (values[1] > MAX_SAMPLES))
return BadValue;
if (!checkonly)
@@ -442,110 +708,35 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
if (!checkonly && common->wcmRotate != value)
wcmRotateTablet(pInfo, value);
- } else if (property == prop_serials)
- {
- return BadValue; /* Read-only */
- } else if (property == prop_strip_buttons)
- {
- CARD8 *values;
-
- if (prop->size != 4 || prop->format != 8)
- return BadValue;
-
- values = (CARD8*)prop->data;
- if (values[0] > WCM_MAX_MOUSE_BUTTONS ||
- values[1] > WCM_MAX_MOUSE_BUTTONS ||
- values[2] > WCM_MAX_MOUSE_BUTTONS ||
- values[3] > WCM_MAX_MOUSE_BUTTONS)
- return BadValue;
-
- if (!checkonly)
- {
- /* FIXME: needs to take AC_* into account */
- priv->striplup = values[0];
- priv->stripldn = values[1];
- priv->striprup = values[2];
- priv->striprdn = values[3];
- }
-
- } else if (property == prop_wheel_buttons)
+ } else if (property == prop_serials)
{
- CARD8 *values;
-
- if (prop->size != 4 || prop->format != 8)
- return BadValue;
+ /* This property is read-only but we need to
+ * set it at runtime. If we get here from wcmUpdateSerial,
+ * we know the serial has ben set internally already, so we
+ * can reply with success. */
+ if (prop->size == 4 && prop->format == 32)
+ if (((CARD32*)prop->data)[3] == priv->cur_serial)
+ return Success;
- values = (CARD8*)prop->data;
-
- if (values[0] > WCM_MAX_MOUSE_BUTTONS ||
- values[1] > WCM_MAX_MOUSE_BUTTONS ||
- values[2] > WCM_MAX_MOUSE_BUTTONS ||
- values[3] > WCM_MAX_MOUSE_BUTTONS)
- return BadValue;
-
- if (!checkonly)
- {
- /* FIXME: needs to take AC_* into account */
- priv->relup = values[0];
- priv->reldn = values[1];
- priv->wheelup = values[2];
- priv->wheeldn = values[3];
- }
- } else if (property == prop_screen)
- {
- /* Long-term, this property should be removed, there's other ways to
- * get the screen resolution. For now, we leave it in for backwards
- * compat */
return BadValue; /* Read-only */
- } else if (property == prop_display)
+ } else if (property == prop_serial_binding)
{
- int nscreens = priv->numScreen;
- INT8 *values;
-
- if (prop->size != 3 || prop->format != 8)
- return BadValue;
+ unsigned int serial;
- values = (INT8*)prop->data;
-
- if (values[1] != TV_NONE)
- nscreens = 2;
-
- if (values[0] < -1 || values[0] >= nscreens)
- return BadValue;
-
- if (values[1] < TV_NONE || values[1] > TV_MAX)
- return BadValue;
-
- if ((values[2] != 0) && (values[2] != 1))
+ if (prop->size != 1 || prop->format != 32)
return BadValue;
if (!checkonly)
{
- if (priv->screen_no != values[0])
- wcmChangeScreen(pInfo, values[0]);
- priv->screen_no = values[0];
-
- if (priv->twinview != values[1])
- {
- int screen = priv->screen_no;
- priv->twinview = values[1];
- priv->numScreen = (priv->twinview) == TV_NONE ? screenInfo.numScreens : 2;
-
- /* Can not restrict the cursor to a particular screen */
- if (!values[1] && (screenInfo.numScreens == 1))
- {
- screen = -1;
- priv->currentScreen = 0;
- DBG(10, priv, "TwinView sets to "
- "TV_NONE: can't change screen_no. \n");
- }
- wcmChangeScreen(pInfo, screen);
- }
-
- priv->wcmMMonitor = values[2];
+ serial = *(CARD32*)prop->data;
+ wcmBindToSerial(pInfo, serial);
}
- } else if (property == prop_cursorprox)
+ } else if (property == prop_strip_buttons)
+ return wcmSetStripProperty(dev, property, prop, checkonly);
+ else if (property == prop_wheel_buttons)
+ return wcmSetWheelProperty(dev, property, prop, checkonly);
+ else if (property == prop_cursorprox)
{
CARD32 value;
@@ -557,26 +748,11 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
value = *(CARD32*)prop->data;
- if (value > 255)
+ if (value > common->wcmMaxDist)
return BadValue;
if (!checkonly)
common->wcmCursorProxoutDist = value;
- } else if (property == prop_capacity)
- {
- INT32 value;
-
- if (prop->size != 1 || prop->format != 32)
- return BadValue;
-
- value = *(INT32*)prop->data;
-
- if ((value < -1) || (value > 5))
- return BadValue;
-
- if (!checkonly)
- common->wcmCapacity = value;
-
} else if (property == prop_threshold)
{
CARD32 value;
@@ -586,7 +762,7 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
value = *(CARD32*)prop->data;
- if ((value < 1) || (value > common->wcmMaxZ))
+ if ((value < 1) || (value > FILTER_PRESSURE_RES))
return BadValue;
if (!checkonly)
@@ -603,7 +779,7 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
if (!checkonly && common->wcmTouch != values[0])
common->wcmTouch = values[0];
- } else if (property == prop_hover)
+ } else if (property == prop_gesture)
{
CARD8 *values = (CARD8*)prop->data;
@@ -613,47 +789,41 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
if ((values[0] != 0) && (values[0] != 1))
return BadValue;
- if (!checkonly && common->wcmTPCButton != !values[0])
- common->wcmTPCButton = !values[0];
- } else if (property == prop_tv_resolutions)
+ if (!checkonly && common->wcmGesture != values[0])
+ common->wcmGesture = values[0];
+ } else if (property == prop_gesture_param)
{
CARD32 *values;
- if (prop->size != 4 || prop->format != 32)
+ if (prop->size != 3 || prop->format != 32)
return BadValue;
values = (CARD32*)prop->data;
- /* non-TwinView settings can not set TwinView RESOLUTION */
- switch(priv->twinview)
+ if (!checkonly)
{
- case TV_NONE:
- /* only reset to 0 allowed */
- if (values[0] || values[1] || values[2] || values[3])
- return BadValue;
- break;
- case TV_ABOVE_BELOW:
- case TV_BELOW_ABOVE:
- if ((values[1] + values[3]) != screenInfo.screens[0]->height)
- return BadValue;
- break;
- case TV_LEFT_RIGHT:
- case TV_RIGHT_LEFT:
- if ((values[0] + values[2]) != screenInfo.screens[0]->width)
- return BadValue;
- break;
+ if (common->wcmGestureParameters.wcmZoomDistance != values[0])
+ common->wcmGestureParameters.wcmZoomDistance = values[0];
+ if (common->wcmGestureParameters.wcmScrollDistance != values[1])
+ common->wcmGestureParameters.wcmScrollDistance = values[1];
+ if (common->wcmGestureParameters.wcmTapTime != values[2])
+ common->wcmGestureParameters.wcmTapTime = values[2];
}
+ } else if (property == prop_hover)
+ {
+ CARD8 *values = (CARD8*)prop->data;
- if (!checkonly)
- {
- priv->tvResolution[0] = values[0];
- priv->tvResolution[1] = values[1];
- priv->tvResolution[2] = values[2];
- priv->tvResolution[3] = values[3];
+ if (prop->size != 1 || prop->format != 8)
+ return BadValue;
- /* reset screen info */
- wcmChangeScreen(pInfo, priv->screen_no);
- }
+ if ((values[0] != 0) && (values[0] != 1))
+ return BadValue;
+
+ if (!IsStylus(priv))
+ return BadMatch;
+
+ if (!checkonly)
+ common->wcmTPCButton = !values[0];
#ifdef DEBUG
} else if (property == prop_debuglevels)
{
@@ -674,112 +844,97 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
#endif
} else if (property == prop_btnactions)
{
- Atom *values;
- int i, j;
- XIPropertyValuePtr val;
-
- if (prop->size != WCM_MAX_MOUSE_BUTTONS || prop->format != 32 ||
- prop->type != XA_ATOM)
+ if (prop->size != WCM_MAX_MOUSE_BUTTONS)
return BadMatch;
+ wcmSetPropertyButtonActions(dev, property, prop, checkonly);
+ } else
+ wcmSetActionProperties(dev, property, prop, checkonly);
- /* How this works:
- * prop_btnactions has a list of atoms stored. Any atom references
- * another property on that device that contains the actual action.
- * If this property changes, all action-properties are queried for
- * their value and their value is stored in priv->key[button].
- *
- * If the button is pressed, the actions are executed.
- *
- * Any button action property needs to be monitored by this property
- * handler too.
- */
-
- values = (Atom*)prop->data;
-
- for (i = 0; i < prop->size; i++)
- {
- if (!values[i])
- continue;
+ return Success;
+}
- if (values[i] == property || !ValidAtom(values[i]))
- return BadValue;
+int wcmGetProperty (DeviceIntPtr dev, Atom property)
+{
+ InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
+ WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
+ WacomCommonPtr common = priv->common;
- if (XIGetDeviceProperty(pInfo->dev, values[i], &val) != Success)
- return BadValue;
- }
+ DBG(10, priv, "\n");
- if (!checkonly)
- {
- /* any action property needs to be registered for this handler. */
- for (i = 0; i < prop->size; i++)
- priv->btn_actions[i] = values[i];
+ if (property == prop_serials)
+ {
+ uint32_t values[4];
- for (i = 0; i < ARRAY_SIZE(priv->keys); i++)
- {
- memset(priv->keys[i], 0, sizeof(priv->keys[i]));
+ values[0] = common->tablet_id;
+ values[1] = priv->old_serial;
+ values[2] = priv->old_device_id;
+ values[3] = priv->cur_serial;
- if (i >= prop->size || !values[i])
- continue;
+ DBG(10, priv, "Update to serial: %d\n", priv->old_serial);
- XIGetDeviceProperty(pInfo->dev, values[i], &val);
+ return XIChangeDeviceProperty(dev, property, XA_INTEGER, 32,
+ PropModeReplace, 4,
+ values, FALSE);
+ }
- for (j = 0; j < val->size; j++)
- priv->keys[i][j] = ((unsigned int*)val->data)[j];
- }
- }
- } else
+ return Success;
+}
+
+static CARD32
+serialTimerFunc(OsTimerPtr timer, CARD32 now, pointer arg)
+{
+ InputInfoPtr pInfo = arg;
+ WacomDevicePtr priv = pInfo->private;
+ XIPropertyValuePtr prop;
+ CARD32 prop_value[4];
+ int sigstate;
+ int rc;
+
+ sigstate = xf86BlockSIGIO();
+
+ rc = XIGetDeviceProperty(pInfo->dev, prop_serials, &prop);
+ if (rc != Success || prop->format != 32 || prop->size != 4)
{
- int i, j;
+ xf86Msg(X_ERROR, "%s: Failed to update serial number.\n",
+ pInfo->name);
+ return 0;
+ }
- /* check all properties used for button actions */
- for (i = 0; i < ARRAY_SIZE(priv->btn_actions); i++)
- if (priv->btn_actions[i] == property)
- break;
+ memcpy(prop_value, prop->data, sizeof(prop_value));
+ prop_value[3] = priv->cur_serial;
- if (i < ARRAY_SIZE(priv->btn_actions))
- {
- CARD32 *data;
- int code;
- int type;
+ XIChangeDeviceProperty(pInfo->dev, prop_serials, XA_INTEGER,
+ prop->format, PropModeReplace,
+ prop->size, prop_value, TRUE);
- if (prop->size >= 255 || prop->format != 32 ||
- prop->type != XA_INTEGER)
- return BadMatch;
+ xf86UnblockSIGIO(sigstate);
- data = (CARD32*)prop->data;
+ return 0;
+}
- for (j = 0;j < prop->size; j++)
- {
- code = data[j] & AC_CODE;
- type = data[j] & AC_TYPE;
-
- switch(type)
- {
- case AC_KEY:
- break;
- case AC_BUTTON:
- if (code > WCM_MAX_MOUSE_BUTTONS)
- return BadValue;
- break;
- case AC_DISPLAYTOGGLE:
- case AC_MODETOGGLE:
- case AC_DBLCLICK:
- break;
- default:
- return BadValue;
- }
-
- if (!checkonly)
- {
- memset(priv->keys[i], 0, sizeof(priv->keys[i]));
- for (j = 0; j < prop->size; j++)
- priv->keys[i][j] = data[j];
- }
- }
- }
- }
+void
+wcmUpdateSerial(InputInfoPtr pInfo, unsigned int serial)
+{
+ WacomDevicePtr priv = pInfo->private;
- return Success;
+ if (priv->cur_serial == serial)
+ return;
+
+ priv->cur_serial = serial;
+
+ /* This function is called during SIGIO. Schedule timer for property
+ * event delivery outside of signal handler. */
+ priv->serial_timer = TimerSet(priv->serial_timer, 0 /* reltime */,
+ 1, serialTimerFunc, pInfo);
}
-#endif /* GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3 */
-/* vim: set noexpandtab shiftwidth=8: */
+
+static void
+wcmBindToSerial(InputInfoPtr pInfo, unsigned int serial)
+{
+ WacomDevicePtr priv = pInfo->private;
+
+ priv->serial = serial;
+
+}
+
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/xf86Wacom.c b/src/xf86Wacom.c
index 2c71dad..d1c149f 100644
--- a/src/xf86Wacom.c
+++ b/src/xf86Wacom.c
@@ -42,7 +42,6 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
-#include <linux/serial.h>
#include "xf86Wacom.h"
#include <xf86_OSproc.h>
@@ -52,6 +51,8 @@
#include <xserver-properties.h>
#include <X11/extensions/XKB.h>
#include <xkbsrv.h>
+#else
+#define XIGetKnownProperty(prop) 0
#endif
static int wcmDevOpen(DeviceIntPtr pWcm);
@@ -80,173 +81,11 @@ static void wcmKbdLedCallback(DeviceIntPtr di, LedCtrl * lcp)
{
}
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5
-static void wcmBellCallback(int pct, DeviceIntPtr di, pointer ctrl, int x)
-{
-}
-#endif
-
static void wcmKbdCtrlCallback(DeviceIntPtr di, KeybdCtrl* ctrl)
{
}
/*****************************************************************************
- * wcmDesktopSize --
- * calculate the whole desktop size
- ****************************************************************************/
-static void wcmDesktopSize(InputInfoPtr pInfo)
-{
- WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
- int i = 0, minX = 0, minY = 0, maxX = 0, maxY = 0;
-
- wcmInitialScreens(pInfo);
- minX = priv->screenTopX[0];
- minY = priv->screenTopY[0];
- maxX = priv->screenBottomX[0];
- maxY = priv->screenBottomY[0];
- if (priv->numScreen != 1)
- {
- for (i = 1; i < priv->numScreen; i++)
- {
- if (priv->screenTopX[i] < minX)
- minX = priv->screenTopX[i];
- if (priv->screenTopY[i] < minY)
- minY = priv->screenTopY[i];
- if (priv->screenBottomX[i] > maxX)
- maxX = priv->screenBottomX[i];
- if (priv->screenBottomY[i] > maxY)
- maxY = priv->screenBottomY[i];
- }
- }
- priv->maxWidth = maxX - minX;
- priv->maxHeight = maxY - minY;
-}
-
-static int wcmInitArea(InputInfoPtr pInfo)
-{
- WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
- WacomToolAreaPtr area = priv->toolarea, inlist;
- WacomCommonPtr common = priv->common;
- double screenRatio, tabletRatio;
- int bottomx = priv->maxX, bottomy = priv->maxY;
-
- DBG(10, priv, "\n");
-
- /* the following 4 blocks verify the box and
- * initialize the area */
- if (priv->topX > bottomx)
- {
- priv->topX = 0;
- }
- area->topX = priv->topX;
-
- if (priv->topY > bottomy)
- {
- priv->topY = 0;
- }
- area->topY = priv->topY;
-
- if (priv->bottomX < priv->topX || !priv->bottomX)
- {
- priv->bottomX = bottomx;
- }
- area->bottomX = priv->bottomX;
-
- if (priv->bottomY < priv->topY || !priv->bottomY)
- {
- priv->bottomY = bottomy;
- }
- area->bottomY = priv->bottomY;
-
- if (priv->twinview != TV_NONE)
- priv->numScreen = 2;
-
- if (priv->screen_no != -1 &&
- (priv->screen_no >= priv->numScreen || priv->screen_no < 0))
- {
- if (priv->twinview == TV_NONE || priv->screen_no != 1)
- {
- xf86Msg(X_ERROR, "%s: invalid screen number %d, resetting to default (-1) \n",
- pInfo->name, priv->screen_no);
- priv->screen_no = -1;
- }
- }
-
- /* need maxWidth and maxHeight for keepshape */
- wcmDesktopSize(pInfo);
-
- /* Maintain aspect ratio to the whole desktop
- * May need to consider a specific screen in multimonitor settings
- */
- if (priv->flags & KEEP_SHAPE_FLAG)
- {
-
- screenRatio = ((double)priv->maxWidth / (double)priv->maxHeight);
- tabletRatio = ((double)(bottomx - priv->topX) /
- (double)(bottomy - priv->topY));
-
- DBG(2, priv, "screenRatio = %.3g, "
- "tabletRatio = %.3g\n", screenRatio, tabletRatio);
-
- if (screenRatio > tabletRatio)
- {
- area->bottomX = priv->bottomX = bottomx;
- area->bottomY = priv->bottomY = (bottomy - priv->topY) *
- tabletRatio / screenRatio + priv->topY;
- }
- else
- {
- area->bottomX = priv->bottomX = (bottomx - priv->topX) *
- screenRatio / tabletRatio + priv->topX;
- area->bottomY = priv->bottomY = bottomy;
- }
- }
- /* end keep shape */
-
- inlist = priv->tool->arealist;
-
- /* The first one in the list is always valid */
- if (area != inlist && wcmAreaListOverlap(area, inlist))
- {
- inlist = priv->tool->arealist;
-
- /* remove this overlapped area from the list */
- for (; inlist; inlist=inlist->next)
- {
- if (inlist->next == area)
- {
- inlist->next = area->next;
- free(area);
- priv->toolarea = NULL;
- break;
- }
- }
-
- /* Remove this device from the common struct */
- if (common->wcmDevices == priv)
- common->wcmDevices = priv->next;
- else
- {
- WacomDevicePtr tmp = common->wcmDevices;
- while(tmp->next && tmp->next != priv)
- tmp = tmp->next;
- if(tmp)
- tmp->next = priv->next;
- }
- xf86Msg(X_ERROR, "%s: Top/Bottom area overlaps with another devices.\n",
- pInfo->name);
- return FALSE;
- }
- xf86Msg(X_PROBED, "%s: top X=%d top Y=%d "
- "bottom X=%d bottom Y=%d "
- "resol X=%d resol Y=%d\n",
- pInfo->name, priv->topX,
- priv->topY, priv->bottomX, priv->bottomY,
- common->wcmResolX, common->wcmResolY);
- return TRUE;
-}
-
-/*****************************************************************************
* wcmVirtualTabletPadding(InputInfoPtr pInfo)
****************************************************************************/
@@ -254,418 +93,268 @@ void wcmVirtualTabletPadding(InputInfoPtr pInfo)
{
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
- /* in multi-screen settings, add the left/top offset given the
- * current multimonitor setup.
- */
-
priv->leftPadding = 0;
priv->topPadding = 0;
- if (!(priv->flags & ABSOLUTE_FLAG)) return;
-
- if ((priv->screen_no != -1) || (priv->twinview != TV_NONE) || (!priv->wcmMMonitor))
- {
- double width, height; /* tablet width in device coords */
- double sw, sh; /* screen width/height in screen coords */
- double offset; /* screen x/y offset in screen coords */
- int screen; /* screen number */
-
- screen = priv->currentScreen;
-
- width = priv->bottomX - priv->topX -priv->tvoffsetX;
- height = priv->bottomY - priv->topY - priv->tvoffsetY;
- sw = priv->screenBottomX[screen] - priv->screenTopX[screen];
- sh = priv->screenBottomY[screen] - priv->screenTopY[screen];
-
- offset = priv->screenTopX[screen];
+ if (!is_absolute(pInfo)) return;
- priv->leftPadding = (int)(offset * width / sw + 0.5);
-
- offset = priv->screenTopY[screen];
-
- priv->topPadding = (int)(offset * height / sh + 0.5);
- }
DBG(10, priv, "x=%d y=%d \n", priv->leftPadding, priv->topPadding);
return;
}
-void
-wcmAdjustArea(const InputInfoPtr pInfo, WacomToolArea *area)
-{
- WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
-
- /* If we're bound to a specific screen, substract the screen's
- * offset from the area coordinates we have here.
- */
- if (priv->screen_no != -1)
- {
- wcmVirtualTabletPadding(pInfo);
- DBG(10, priv, "padding is %d/%d\n", priv->leftPadding, priv->topPadding);
- area->topX -= priv->leftPadding;
- area->bottomX -= priv->leftPadding;
- area->topY -= priv->topPadding;
- area->bottomY -= priv->topPadding;
- DBG(10, priv, "updated area is %d/%d → %d/%d\n",
- area->topX, area->topY, area->bottomX, area->bottomY);
- }
-}
-
/*****************************************************************************
- * wcmVirtualTabletSize(InputInfoPtr pInfo)
+ * wcmInitialToolSize --
+ * Initialize logical size and resolution for individual tool.
****************************************************************************/
-void wcmVirtualTabletSize(InputInfoPtr pInfo)
+static void wcmInitialToolSize(InputInfoPtr pInfo)
{
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+ WacomCommonPtr common = priv->common;
- if (!(priv->flags & ABSOLUTE_FLAG))
+ /* assign max and resolution here since we don't get them during
+ * the configuration stage */
+ if (IsTouch(priv))
{
- priv->sizeX = priv->bottomX - priv->topX;
- priv->sizeY = priv->bottomY - priv->topY;
- DBG(10, priv, "relative device, using size of %d/%d\n", priv->sizeX, priv->sizeY);
-
- return;
+ priv->maxX = common->wcmMaxTouchX;
+ priv->maxY = common->wcmMaxTouchY;
+ priv->resolX = common->wcmTouchResolX;
+ priv->resolY = common->wcmTouchResolY;
}
- /* given a three monitor setup
- * offset
- * __________ /__________ __________
- * | || || |
- * | || || |
- * | A || B || C |
- * |__________||__________||__________|
- *
- * this function calculates the virtual size of the tablet by
- * mapping the actual size into an axis range that is all three
- * monitors in device coordinates taken together.
- * in the simplest case, with 3 identical monitors, sizeX would be
- * (3 * actual size).
- *
- * coments describe for example of screen_no = 1 (Screen B)
- */
-
- /* This is the actual tablet size in device coords */
- priv->sizeX = priv->bottomX - priv->topX - priv->tvoffsetX;
- priv->sizeY = priv->bottomY - priv->topY - priv->tvoffsetY;
-
- if ((priv->screen_no != -1) || (priv->twinview != TV_NONE) || (!priv->wcmMMonitor))
+ else
{
- double width, height; /* screen width, height */
- double offset; /* screen x or y offset from origin */
- double remainder; /* screen remainer on right-most screens */
- double tabletSize;
- int screen = priv->currentScreen;
-
- width = priv->screenBottomX[screen] - priv->screenTopX[screen];
- offset = priv->screenTopX[screen];
- tabletSize = priv->sizeX;
- /* width left over right of the screen */
- remainder = priv->maxWidth - priv->screenBottomX[screen];
-
- /* add screen A size in device coordinates */
- priv->sizeX += (int)((offset * tabletSize) / width + 0.5);
-
- /* add screen C size in device coordinates */
- priv->sizeX += (int)((remainder * tabletSize) / width + 0.5);
-
- tabletSize = priv->sizeY;
+ priv->maxX = common->wcmMaxX;
+ priv->maxY = common->wcmMaxY;
+ priv->resolX = common->wcmResolX;
+ priv->resolY = common->wcmResolY;
+ }
- offset = priv->screenTopY[screen];
- height = priv->screenBottomY[screen] - priv->screenTopY[screen];
- /* height left over bottom of the screen */
- remainder = priv->maxHeight - priv->screenBottomY[screen];
+ if (!priv->bottomX)
+ priv->bottomX = priv->maxX;
+ if (!priv->bottomY)
+ priv->bottomY = priv->maxY;
- priv->sizeY += (int)(offset * tabletSize / height + 0.5);
- priv->sizeY += (int)((remainder * tabletSize) / height + 0.5);
- }
- DBG(10, priv, "x=%d y=%d \n", priv->sizeX, priv->sizeY);
return;
}
-/*****************************************************************************
- * wcmInitialCoordinates
- ****************************************************************************/
-
-void wcmInitialCoordinates(InputInfoPtr pInfo, int axis)
+static int
+wcmInitAxes(DeviceIntPtr pWcm)
{
+ InputInfoPtr pInfo = (InputInfoPtr)pWcm->public.devicePrivate;
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
- int topx = 0, topy = 0, resolution_x, resolution_y;
- int bottomx = priv->maxX, bottomy = priv->maxY;
- wcmMappingFactor(pInfo);
+ Atom label;
+ int min, max, min_res, max_res, res;
+ int mode;
- /* wcmMappingFactor calls wcmVirtualTabletSize. so once we're here,
- * sizeX contains the total width in device coordinates accounting
- * for multiple screens (not the _actual width of the tablet, see
- * wcmVirtualTabletSize)
- */
- if (priv->flags & ABSOLUTE_FLAG)
- {
- topx = priv->topX;
- topy = priv->topY;
- bottomx = priv->sizeX + priv->topX;
- bottomy = priv->sizeY + priv->topY;
+ /* first valuator: x */
+ label = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X);
+ min = priv->topX;
+ max = priv->bottomX;
+ min_res = 0;
+ max_res = priv->resolX;
+ res = priv->resolX;
+ mode = Absolute;
- if (priv->twinview != TV_NONE)
- {
- if (priv->currentScreen == 1)
- {
- topx += priv->tvoffsetX;
- topy += priv->tvoffsetY;
- } else if (priv->currentScreen == 0)
- {
- bottomx -= priv->tvoffsetX;
- bottomy -= priv->tvoffsetY;
- }
- }
- }
- resolution_x = priv->resolX;
- resolution_y = priv->resolY;
+ InitValuatorAxisStruct(pInfo->dev, 0,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
+ label,
+#endif
+ min, max, res, min_res, max_res
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ , mode
+#endif
+ );
+
+ /* second valuator: y */
+ label = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y);
+ min = priv->topY;
+ max = priv->bottomY;
+ min_res = 0;
+ max_res = priv->resolY;
+ res = priv->resolY;
+ mode = Absolute;
+
+ InitValuatorAxisStruct(pInfo->dev, 1,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
+ label,
+#endif
+ min, max, res, min_res, max_res
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ , mode
+#endif
+ );
+
+
+ /* third valuator: pressure */
- if (common->wcmScaling)
+ mode = Absolute;
+ min_res = max_res = res = 1;
+ min = 0;
+
+ if (!IsPad(priv))
{
- /* In case wcmDevConvert didn't get called */
- topx = 0;
- bottomx = (int)((double)priv->sizeX * priv->factorX + 0.5);
- resolution_x = (int)((double)resolution_x * priv->factorX + 0.5);
-
- topy = 0;
- bottomy = (int)((double)priv->sizeY * priv->factorY + 0.5);
- resolution_y = (int)((double)resolution_y * priv->factorY + 0.5);
+ label = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_PRESSURE);
+ /* pressure normalized to FILTER_PRESSURE_RES */
+ max = FILTER_PRESSURE_RES;
+ } else {
+ /* The pad doesn't have a pressure axis, so initialise third
+ * axis as unknown absolute axis on the pad. This way, we
+ * can leave the strip/abswheel axes on later axes and don't
+ * run the danger of clients misinterpreting the axis info
+ */
+ label = None;
+ max = 1;
}
- switch(axis)
- {
- case 0:
- InitValuatorAxisStruct(pInfo->dev, 0,
+
+ InitValuatorAxisStruct(pInfo->dev, 2,
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X),
+ label,
#endif
- topx, bottomx,
- resolution_x, 0, resolution_x
+ min, max, res, min_res, max_res
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
+ , mode
#endif
- );
- break;
- case 1:
- InitValuatorAxisStruct(pInfo->dev, 1,
+ );
+
+ /* fourth valuator: tilt-x, cursor:z-rotation, pad:strip-x */
+
+ if (IsCursor(priv))
+ {
+ label = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_RZ);
+ min = MIN_ROTATION;
+ max = MIN_ROTATION + MAX_ROTATION_RANGE - 1;
+ min_res = max_res = res = 1;
+ mode = Absolute;
+ } else if (IsPad(priv))
+ {
+ label = None; /* XXX: what is this axis? */
+ min = 0;
+ max = 1; /* dummy value if !HasFeature(WCM_STRIP) */
+ min_res = max_res = res = 1;
+ mode = Absolute;
+ if (TabletHasFeature(common, WCM_STRIP))
+ max = common->wcmMaxStripX;
+ } else
+ {
+ label = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_TILT_X),
+ min = -64;
+ max = 63;
+ min_res = max_res = res = 1;
+ mode = Absolute;
+ }
+
+ InitValuatorAxisStruct(pInfo->dev, 3,
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y),
+ label,
#endif
- topy, bottomy,
- resolution_y, 0, resolution_y
+ min, max, res, min_res, max_res
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
+ , mode
#endif
- );
- break;
- default:
- xf86Msg(X_ERROR, "%s: Cannot initialize axis %d.\n", pInfo->name, axis);
- break;
- }
-
- return;
-}
+ );
-/* Define our own keymap so we can send key-events with our own device and not
- * rely on inputInfo.keyboard */
-static KeySym keymap[] = {
- /* 0x00 */ NoSymbol, NoSymbol, XK_Escape, NoSymbol,
- /* 0x02 */ XK_1, XK_exclam, XK_2, XK_at,
- /* 0x04 */ XK_3, XK_numbersign, XK_4, XK_dollar,
- /* 0x06 */ XK_5, XK_percent, XK_6, XK_asciicircum,
- /* 0x08 */ XK_7, XK_ampersand, XK_8, XK_asterisk,
- /* 0x0a */ XK_9, XK_parenleft, XK_0, XK_parenright,
- /* 0x0c */ XK_minus, XK_underscore, XK_equal, XK_plus,
- /* 0x0e */ XK_BackSpace, NoSymbol, XK_Tab, XK_ISO_Left_Tab,
- /* 0x10 */ XK_q, NoSymbol, XK_w, NoSymbol,
- /* 0x12 */ XK_e, NoSymbol, XK_r, NoSymbol,
- /* 0x14 */ XK_t, NoSymbol, XK_y, NoSymbol,
- /* 0x16 */ XK_u, NoSymbol, XK_i, NoSymbol,
- /* 0x18 */ XK_o, NoSymbol, XK_p, NoSymbol,
- /* 0x1a */ XK_bracketleft, XK_braceleft, XK_bracketright, XK_braceright,
- /* 0x1c */ XK_Return, NoSymbol, XK_Control_L, NoSymbol,
- /* 0x1e */ XK_a, NoSymbol, XK_s, NoSymbol,
- /* 0x20 */ XK_d, NoSymbol, XK_f, NoSymbol,
- /* 0x22 */ XK_g, NoSymbol, XK_h, NoSymbol,
- /* 0x24 */ XK_j, NoSymbol, XK_k, NoSymbol,
- /* 0x26 */ XK_l, NoSymbol, XK_semicolon, XK_colon,
- /* 0x28 */ XK_quoteright, XK_quotedbl, XK_quoteleft, XK_asciitilde,
- /* 0x2a */ XK_Shift_L, NoSymbol, XK_backslash, XK_bar,
- /* 0x2c */ XK_z, NoSymbol, XK_x, NoSymbol,
- /* 0x2e */ XK_c, NoSymbol, XK_v, NoSymbol,
- /* 0x30 */ XK_b, NoSymbol, XK_n, NoSymbol,
- /* 0x32 */ XK_m, NoSymbol, XK_comma, XK_less,
- /* 0x34 */ XK_period, XK_greater, XK_slash, XK_question,
- /* 0x36 */ XK_Shift_R, NoSymbol, XK_KP_Multiply, NoSymbol,
- /* 0x38 */ XK_Alt_L, XK_Meta_L, XK_space, NoSymbol,
- /* 0x3a */ XK_Caps_Lock, NoSymbol, XK_F1, NoSymbol,
- /* 0x3c */ XK_F2, NoSymbol, XK_F3, NoSymbol,
- /* 0x3e */ XK_F4, NoSymbol, XK_F5, NoSymbol,
- /* 0x40 */ XK_F6, NoSymbol, XK_F7, NoSymbol,
- /* 0x42 */ XK_F8, NoSymbol, XK_F9, NoSymbol,
- /* 0x44 */ XK_F10, NoSymbol, XK_Num_Lock, NoSymbol,
- /* 0x46 */ XK_Scroll_Lock, NoSymbol, XK_KP_Home, XK_KP_7,
- /* 0x48 */ XK_KP_Up, XK_KP_8, XK_KP_Prior, XK_KP_9,
- /* 0x4a */ XK_KP_Subtract, NoSymbol, XK_KP_Left, XK_KP_4,
- /* 0x4c */ XK_KP_Begin, XK_KP_5, XK_KP_Right, XK_KP_6,
- /* 0x4e */ XK_KP_Add, NoSymbol, XK_KP_End, XK_KP_1,
- /* 0x50 */ XK_KP_Down, XK_KP_2, XK_KP_Next, XK_KP_3,
- /* 0x52 */ XK_KP_Insert, XK_KP_0, XK_KP_Delete, XK_KP_Decimal,
- /* 0x54 */ NoSymbol, NoSymbol, XK_F13, NoSymbol,
- /* 0x56 */ XK_less, XK_greater, XK_F11, NoSymbol,
- /* 0x58 */ XK_F12, NoSymbol, XK_F14, NoSymbol,
- /* 0x5a */ XK_F15, NoSymbol, XK_F16, NoSymbol,
- /* 0x5c */ XK_F17, NoSymbol, XK_F18, NoSymbol,
- /* 0x5e */ XK_F19, NoSymbol, XK_F20, NoSymbol,
- /* 0x60 */ XK_KP_Enter, NoSymbol, XK_Control_R, NoSymbol,
- /* 0x62 */ XK_KP_Divide, NoSymbol, XK_Print, XK_Sys_Req,
- /* 0x64 */ XK_Alt_R, XK_Meta_R, NoSymbol, NoSymbol,
- /* 0x66 */ XK_Home, NoSymbol, XK_Up, NoSymbol,
- /* 0x68 */ XK_Prior, NoSymbol, XK_Left, NoSymbol,
- /* 0x6a */ XK_Right, NoSymbol, XK_End, NoSymbol,
- /* 0x6c */ XK_Down, NoSymbol, XK_Next, NoSymbol,
- /* 0x6e */ XK_Insert, NoSymbol, XK_Delete, NoSymbol,
- /* 0x70 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x72 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x74 */ NoSymbol, NoSymbol, XK_KP_Equal, NoSymbol,
- /* 0x76 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x78 */ XK_F21, NoSymbol, XK_F22, NoSymbol,
- /* 0x7a */ XK_F23, NoSymbol, XK_F24, NoSymbol,
- /* 0x7c */ XK_KP_Separator, NoSymbol, XK_Meta_L, NoSymbol,
- /* 0x7e */ XK_Meta_R, NoSymbol, XK_Multi_key, NoSymbol,
- /* 0x80 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x82 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x84 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x86 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x88 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x8a */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x8c */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x8e */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x90 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x92 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x94 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x96 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x98 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x9a */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x9c */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0x9e */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xa0 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xa2 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xa4 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xa6 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xa8 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xaa */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xac */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xae */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xb0 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xb2 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xb4 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xb6 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xb8 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xba */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xbc */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xbe */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xc0 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xc2 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xc4 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xc6 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xc8 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xca */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xcc */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xce */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xd0 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xd2 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xd4 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xd6 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xd8 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xda */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xdc */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xde */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xe0 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xe2 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xe4 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xe6 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xe8 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xea */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xec */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xee */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xf0 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xf2 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xf4 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol,
- /* 0xf6 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol
-};
+ /* fifth valuator: tilt-y, cursor:throttle, pad:strip-y */
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5
-static struct { KeySym keysym; CARD8 mask; } keymod[] = {
- { XK_Shift_L, ShiftMask },
- { XK_Shift_R, ShiftMask },
- { XK_Control_L, ControlMask },
- { XK_Control_R, ControlMask },
- { XK_Caps_Lock, LockMask },
- { XK_Alt_L, Mod1Mask }, /*AltMask*/
- { XK_Alt_R, Mod1Mask }, /*AltMask*/
- { XK_Num_Lock, Mod2Mask }, /*NumLockMask*/
- { XK_Scroll_Lock, Mod5Mask }, /*ScrollLockMask*/
- { XK_Mode_switch, Mod3Mask }, /*AltMask*/
- { NoSymbol, 0 }
-};
+ if (IsCursor(priv))
+ {
+ label = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_THROTTLE);
+ min = -1023;
+ max = 1023;
+ min_res = max_res = res = 1;
+ mode = Absolute;
+ } else if (IsPad(priv))
+ {
+ label = None; /* XXX: what is this axis? */
+ min = 0;
+ max = 1; /* dummy value if !HasFeature(WCM_STRIP) */
+ min_res = max_res = res = 1;
+ mode = Absolute;
+ if (TabletHasFeature(common, WCM_STRIP))
+ max = common->wcmMaxStripY;
+ } else
+ {
+ label = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_TILT_Y);
+ min = -64;
+ max = 63;
+ min_res = max_res = res = 1;
+ mode = Absolute;
+ }
+
+ InitValuatorAxisStruct(pInfo->dev, 4,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
+ label,
+#endif
+ min, max, res, min_res, max_res
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ , mode
#endif
+ );
-/*****************************************************************************
- * wcmInitialToolSize --
- * Initialize logical size and resolution for individual tool.
- ****************************************************************************/
+ /* sixth valuator: airbrush: abs-wheel, artpen: rotation, pad:abs-wheel */
-static void wcmInitialToolSize(InputInfoPtr pInfo)
-{
- WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
- WacomCommonPtr common = priv->common;
- WacomToolPtr toollist = common->wcmTool;
- WacomToolAreaPtr arealist;
-
- /* assign max and resolution here since we don't get them during
- * the configuration stage */
- if (IsTouch(priv))
+ if (IsStylus(priv))
{
- priv->maxX = common->wcmMaxTouchX;
- priv->maxY = common->wcmMaxTouchY;
- priv->resolX = common->wcmTouchResolX;
- priv->resolY = common->wcmTouchResolY;
- }
- else
+ label = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_WHEEL);
+ max = MAX_ROTATION_RANGE + MIN_ROTATION - 1;
+ min = MIN_ROTATION;
+ min_res = max_res = res = 1;
+ mode = Absolute;
+ } else if ((TabletHasFeature(common, WCM_RING)) && IsPad(priv))
{
- priv->maxX = common->wcmMaxX;
- priv->maxY = common->wcmMaxY;
- priv->resolX = common->wcmResolX;
- priv->resolY = common->wcmResolY;
+ /* Touch ring */
+ label = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_WHEEL);
+ min = MIN_PAD_RING;
+ max = MAX_PAD_RING;
+ min_res = max_res = res = 1;
+ mode = Absolute;
}
- for (; toollist; toollist=toollist->next)
+ InitValuatorAxisStruct(pInfo->dev, 5,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
+ label,
+#endif
+ min, max, res, min_res, max_res
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ , mode
+#endif
+ );
+
+ /* seventh valuator: abswheel2 */
+ if ((TabletHasFeature(common, WCM_DUALRING)) && IsPad(priv))
{
- arealist = toollist->arealist;
- for (; arealist; arealist=arealist->next)
- {
- if (!arealist->bottomX)
- arealist->bottomX = priv->maxX;
- if (!arealist->bottomY)
- arealist->bottomY = priv->maxY;
- }
+ /* Second touch ring */
+ label = None;
+ min = MIN_PAD_RING;
+ max = MAX_PAD_RING;
+ min_res = max_res = res = 1;
+ mode = Absolute;
+
+ InitValuatorAxisStruct(pInfo->dev, 6,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
+ label,
+#endif
+ min, max, res, min_res, max_res
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ , mode
+#endif
+ );
}
- return;
+ return TRUE;
}
/*****************************************************************************
- * wcmRegisterX11Devices --
- * Register the X11 input devices with X11 core.
+ * wcmDevInit --
+ * Set up the device's buttons, axes and keys
****************************************************************************/
-static int wcmRegisterX11Devices (InputInfoPtr pInfo)
+static int wcmDevInit(DeviceIntPtr pWcm)
{
+ InputInfoPtr pInfo = (InputInfoPtr)pWcm->public.devicePrivate;
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
- WacomCommonPtr common = priv->common;
unsigned char butmap[WCM_MAX_BUTTONS+1];
int nbaxes, nbbuttons, nbkeys;
int loop;
@@ -681,6 +370,9 @@ static int wcmRegisterX11Devices (InputInfoPtr pInfo)
nbaxes = priv->naxes; /* X, Y, Pressure, Tilt-X, Tilt-Y, Wheel */
nbbuttons = priv->nbuttons; /* Use actual number of buttons, if possible */
+ if (IsPad(priv) && TabletHasFeature(priv->common, WCM_DUALRING))
+ nbaxes = priv->naxes = nbaxes + 1; /* ABS wheel 2 */
+
/* if more than 3 buttons, offset by the four scroll buttons,
* otherwise, alloc 7 buttons for scroll wheel. */
nbbuttons = (nbbuttons > 3) ? nbbuttons + 4 : 7;
@@ -700,9 +392,7 @@ static int wcmRegisterX11Devices (InputInfoPtr pInfo)
DBG(10, priv,
"(%s) %d buttons, %d keys, %d axes\n",
- IsStylus(priv) ? "stylus" :
- IsCursor(priv) ? "cursor" :
- IsPad(priv) ? "pad" : "eraser",
+ pInfo->type_name,
nbbuttons, nbkeys, nbaxes);
for(loop=1; loop<=nbbuttons; loop++)
@@ -738,8 +428,8 @@ static int wcmRegisterX11Devices (InputInfoPtr pInfo)
return FALSE;
}
- if (!nbaxes || nbaxes > 6)
- nbaxes = priv->naxes = 6;
+ if (!nbaxes || nbaxes > 7)
+ nbaxes = priv->naxes = 7;
/* axis_labels is just zeros, we set up each valuator with the
* correct property later */
@@ -747,237 +437,36 @@ static int wcmRegisterX11Devices (InputInfoPtr pInfo)
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
axis_labels,
#endif
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3
- GetMotionHistory,
-#endif
GetMotionHistorySize(),
- ((priv->flags & ABSOLUTE_FLAG) ?
- Absolute : Relative) |
- OutOfProximity ) == FALSE)
+ (is_absolute(pInfo) ? Absolute : Relative) | OutOfProximity) == FALSE)
{
xf86Msg(X_ERROR, "%s: unable to allocate Valuator class device\n", pInfo->name);
return FALSE;
}
- /* only initial KeyClass and LedFeedbackClass once */
- if (!priv->wcmInitKeyClassCount)
- {
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5
- if (nbkeys)
- {
- KeySymsRec wacom_keysyms;
- CARD8 modmap[MAP_LENGTH];
- int i,j;
-
- memset(modmap, 0, sizeof(modmap));
- for(i=0; keymod[i].keysym != NoSymbol; i++)
- for(j=8; j<256; j++)
- if(keymap[(j-8)*2] == keymod[i].keysym)
- modmap[j] = keymod[i].mask;
-
- /* There seems to be a long-standing misunderstanding about
- * how a keymap should be defined. All tablet drivers from
- * stock X11 source tree are doing it wrong: they leave first
- * 8 keysyms as VoidSymbol's, and are passing 8 as minimum
- * key code. But if you look at SetKeySymsMap() from
- * programs/Xserver/dix/devices.c you will see that
- * Xserver does not require first 8 keysyms; it supposes
- * that the map begins at minKeyCode.
- *
- * It could be that this assumption is a leftover from
- * earlier XFree86 versions, but that's out of our scope.
- * This also means that no keys on extended input devices
- * with their own keycodes (e.g. tablets) were EVER used.
- */
- wacom_keysyms.map = keymap;
- /* minKeyCode = 8 because this is the min legal key code */
- wacom_keysyms.minKeyCode = 8;
- wacom_keysyms.maxKeyCode = 255;
- wacom_keysyms.mapWidth = 2;
- if (InitKeyClassDeviceStruct(pInfo->dev, &wacom_keysyms, modmap) == FALSE)
- {
- xf86Msg(X_ERROR, "%s: unable to init key class device\n", pInfo->name);
- return FALSE;
- }
- }
-
- if(InitKbdFeedbackClassDeviceStruct(pInfo->dev, wcmBellCallback,
- wcmKbdCtrlCallback) == FALSE) {
- xf86Msg(X_ERROR, "%s: unable to init kbd feedback device struct\n", pInfo->name);
- return FALSE;
- }
-#elif GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- if (InitKeyboardDeviceStruct(pInfo->dev, NULL, NULL, wcmKbdCtrlCallback)) {
-#define SYMS_PER_KEY 2
- KeySymsRec syms;
- CARD8 modmap[MAP_LENGTH];
- int num_keys = XkbMaxLegalKeyCode - XkbMinLegalKeyCode + 1;
-
- syms.map = keymap;
- syms.mapWidth = SYMS_PER_KEY;
- syms.minKeyCode = XkbMinLegalKeyCode;
- syms.maxKeyCode = XkbMaxLegalKeyCode;
-
- memset(modmap, 0, sizeof(modmap));
- modmap[XkbMinLegalKeyCode + 2] = ShiftMask;
- XkbApplyMappingChange(pInfo->dev, &syms, syms.minKeyCode, num_keys, NULL, // modmap,
- serverClient);
- } else
- {
- xf86Msg(X_ERROR, "%s: unable to init kbd device struct\n", pInfo->name);
- return FALSE;
- }
-#endif
- if(InitLedFeedbackClassDeviceStruct (pInfo->dev, wcmKbdLedCallback) == FALSE) {
- xf86Msg(X_ERROR, "%s: unable to init led feedback device struct\n", pInfo->name);
- return FALSE;
- }
- }
-
- wcmInitialToolSize(pInfo);
-
- if (wcmInitArea(pInfo) == FALSE)
- {
- return FALSE;
- }
-
- /* Rotation rotates the Max X and Y */
- wcmRotateTablet(pInfo, common->wcmRotate);
-
- /* pressure */
- InitValuatorAxisStruct(pInfo->dev, 2,
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- XIGetKnownProperty(AXIS_LABEL_PROP_ABS_PRESSURE),
-#endif
- 0, common->wcmMaxZ, 1, 1, 1
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
-#endif
- );
-
- if (IsCursor(priv))
- {
- /* z-rot and throttle */
- InitValuatorAxisStruct(pInfo->dev, 3,
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- XIGetKnownProperty(AXIS_LABEL_PROP_ABS_RZ),
-#endif
- -900, 899, 1, 1, 1
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
-#endif
- );
- InitValuatorAxisStruct(pInfo->dev, 4,
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- XIGetKnownProperty(AXIS_LABEL_PROP_ABS_THROTTLE),
-#endif
- -1023, 1023, 1, 1, 1
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
-#endif
- );
- }
- else if (IsPad(priv))
- {
- /* strip-x and strip-y */
- if (strstr(common->wcmModel->name, "Intuos3") ||
- strstr(common->wcmModel->name, "CintiqV5"))
- {
- InitValuatorAxisStruct(pInfo->dev, 3,
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- 0, /* XXX what is this axis?*/
-#endif
- 0, common->wcmMaxStripX, 1, 1, 1
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
-#endif
- );
- InitValuatorAxisStruct(pInfo->dev, 4,
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- 0, /* XXX what is this axis?*/
-#endif
- 0, common->wcmMaxStripY, 1, 1, 1
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
-#endif
- );
- }
+ if (!InitKeyboardDeviceStruct(pInfo->dev, NULL, NULL, wcmKbdCtrlCallback)) {
+ xf86Msg(X_ERROR, "%s: unable to init kbd device struct\n", pInfo->name);
+ return FALSE;
}
- else
- {
- /* tilt-x and tilt-y */
- InitValuatorAxisStruct(pInfo->dev, 3,
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- XIGetKnownProperty(AXIS_LABEL_PROP_ABS_TILT_X),
-#endif
- -64, 63, 1, 1, 1
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
-#endif
- );
- InitValuatorAxisStruct(pInfo->dev, 4,
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- XIGetKnownProperty(AXIS_LABEL_PROP_ABS_TILT_Y),
-#endif
- -64, 63, 1, 1, 1
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
#endif
- );
+ if(InitLedFeedbackClassDeviceStruct (pInfo->dev, wcmKbdLedCallback) == FALSE) {
+ xf86Msg(X_ERROR, "%s: unable to init led feedback device struct\n", pInfo->name);
+ return FALSE;
}
- if ((strstr(common->wcmModel->name, "Intuos3") ||
- strstr(common->wcmModel->name, "CintiqV5") ||
- strstr(common->wcmModel->name, "Intuos4"))
- && IsStylus(priv))
- /* Art Marker Pen rotation */
- InitValuatorAxisStruct(pInfo->dev, 5,
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- 0, /* XXX what is this axis?*/
-#endif
- -900, 899, 1, 1, 1
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
-#endif
- );
- else if ((strstr(common->wcmModel->name, "Bamboo") ||
- strstr(common->wcmModel->name, "Intuos4"))
- && IsPad(priv))
- /* Touch ring */
- InitValuatorAxisStruct(pInfo->dev, 5,
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- 0, /* XXX what is this axis?*/
-#endif
- 0, 71, 1, 1, 1
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
-#endif
- );
- else
+ if (!IsPad(priv))
{
- /* absolute wheel */
- InitValuatorAxisStruct(pInfo->dev, 5,
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
- XIGetKnownProperty(AXIS_LABEL_PROP_ABS_WHEEL),
-#endif
- 0, 1023, 1, 1, 1
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
- , Absolute
-#endif
- );
+ wcmInitialToolSize(pInfo);
+ wcmMappingFactor(pInfo);
}
- if (IsTouch(priv))
- {
- /* hard prox out */
- priv->hardProx = 0;
- }
+ if (!wcmInitAxes(pWcm))
+ return FALSE;
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
InitWcmDeviceProperties(pInfo);
- XIRegisterPropertyHandler(pInfo->dev, wcmSetProperty, NULL, NULL);
-#endif
+ XIRegisterPropertyHandler(pInfo->dev, wcmSetProperty, wcmGetProperty, wcmDeleteProperty);
return TRUE;
}
@@ -999,10 +488,17 @@ Bool wcmIsWacomDevice (char* fname)
SYSCALL(close(fd));
- if (id.vendor == WACOM_VENDOR_ID)
- return TRUE;
- else
- return FALSE;
+ switch(id.vendor)
+ {
+ case WACOM_VENDOR_ID:
+ case WALTOP_VENDOR_ID:
+ case HANWANG_VENDOR_ID:
+ case LENOVO_VENDOR_ID:
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
}
/*****************************************************************************
@@ -1042,6 +538,7 @@ char *wcmEventAutoDevProbe (InputInfoPtr pInfo)
}
xf86Msg(X_ERROR, "%s: no Wacom event device found (checked %d nodes, waited %d msec)\n",
pInfo->name, i + 1, wait);
+ xf86Msg(X_ERROR, "%s: unable to probe device\n", pInfo->name);
return FALSE;
}
@@ -1049,14 +546,10 @@ char *wcmEventAutoDevProbe (InputInfoPtr pInfo)
* wcmOpen --
****************************************************************************/
-static Bool wcmOpen(InputInfoPtr pInfo)
+Bool wcmOpen(InputInfoPtr pInfo)
{
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
- char id[BUFFER_SIZE];
- float version;
- int rc;
- struct serial_struct ser;
DBG(1, priv, "opening device file\n");
@@ -1064,44 +557,10 @@ static Bool wcmOpen(InputInfoPtr pInfo)
if (pInfo->fd < 0)
{
xf86Msg(X_ERROR, "%s: Error opening %s (%s)\n", pInfo->name,
- common->wcmDevice, strerror(errno));
+ common->device_path, strerror(errno));
return !Success;
}
- rc = ioctl(pInfo->fd, TIOCGSERIAL, &ser);
-
- /* we initialized wcmDeviceClasses to USB
- * Bluetooth is also considered as USB */
- if (rc == 0) /* serial device */
- {
- /* only ISDV4 are supported on X server 1.7 and later */
- common->wcmForceDevice=DEVICE_ISDV4;
- common->wcmDevCls = &gWacomISDV4Device;
-
- /* Tablet PC buttons on by default */
- common->wcmTPCButtonDefault = 1;
- }
- else
- {
- /* Detect USB device class */
- if ((&gWacomUSBDevice)->Detect(pInfo))
- common->wcmDevCls = &gWacomUSBDevice;
- else
- {
- xf86Msg(X_ERROR, "%s: wcmOpen found undetectable "
- " %s \n", pInfo->name, common->wcmDevice);
- return !Success;
- }
- }
-
- /* Initialize the tablet */
- if(common->wcmDevCls->Init(pInfo, id, &version) != Success ||
- wcmInitTablet(pInfo, id, version) != Success)
- {
- xf86CloseSerial(pInfo->fd);
- pInfo->fd = -1;
- return !Success;
- }
return Success;
}
@@ -1115,27 +574,17 @@ static int wcmDevOpen(DeviceIntPtr pWcm)
InputInfoPtr pInfo = (InputInfoPtr)pWcm->public.devicePrivate;
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
WacomCommonPtr common = priv->common;
+ WacomModelPtr model = common->wcmModel;
struct stat st;
DBG(10, priv, "\n");
- /* Device has been open and not autoprobed */
- if (priv->wcmDevOpenCount)
- return TRUE;
-
/* open file, if not already open */
if (common->fd_refs == 0)
{
- /* Autoprobe if necessary */
- if ((common->wcmFlags & AUTODEV_FLAG) &&
- !(common->wcmDevice = wcmEventAutoDevProbe (pInfo)))
- xf86Msg(X_ERROR, "%s: Cannot probe device\n", pInfo->name);
-
- if ((wcmOpen (pInfo) != Success) || (pInfo->fd < 0) ||
- !common->wcmDevice)
+ if ((wcmOpen (pInfo) != Success) || !common->device_path)
{
- DBG(1, priv, "Failed to open "
- "device (fd=%d)\n", pInfo->fd);
+ DBG(1, priv, "Failed to open device (fd=%d)\n", pInfo->fd);
if (pInfo->fd >= 0)
{
DBG(1, priv, "Closing device\n");
@@ -1148,8 +597,7 @@ static int wcmDevOpen(DeviceIntPtr pWcm)
if (fstat(pInfo->fd, &st) == -1)
{
/* can not access major/minor */
- DBG(1, priv, "stat failed (%s). "
- "cannot check status.\n", strerror(errno));
+ DBG(1, priv, "stat failed (%s).\n", strerror(errno));
/* older systems don't support the required ioctl.
* So, we have to let it pass */
@@ -1168,8 +616,9 @@ static int wcmDevOpen(DeviceIntPtr pWcm)
common->fd_refs++;
}
- if (!wcmRegisterX11Devices (pInfo))
- return FALSE;
+ /* start the tablet data */
+ if (model->Start && (model->Start(pInfo) != Success))
+ return !Success;
return TRUE;
}
@@ -1231,8 +680,7 @@ void wcmReadPacket(InputInfoPtr pInfo)
remaining = sizeof(common->buffer) - common->bufpos;
- DBG(1, common, "pos=%d"
- " remaining=%d\n", common->bufpos, remaining);
+ DBG(1, common, "pos=%d remaining=%d\n", common->bufpos, remaining);
/* fill buffer with as much data as we can handle */
len = xf86ReadSerial(pInfo->fd,
@@ -1240,16 +688,10 @@ void wcmReadPacket(InputInfoPtr pInfo)
if (len <= 0)
{
- /* In case of error, we assume the device has been
- * disconnected. So we close it and iterate over all
- * wcmDevices to actually close associated devices. */
- WacomDevicePtr wDev = common->wcmDevices;
- for(; wDev; wDev = wDev->next)
- {
- if (wDev->pInfo->fd >= 0)
- wcmDevProc(wDev->pInfo->dev, DEVICE_OFF);
- }
- xf86Msg(X_ERROR, "%s: Error reading wacom device : %s\n", pInfo->name, strerror(errno));
+ /* for all other errors, hope that the hotplugging code will
+ * remove the device */
+ if (errno != EAGAIN && errno != EINTR)
+ xf86Msg(X_ERROR, "%s: Error reading wacom device : %s\n", pInfo->name, strerror(errno));
return;
}
@@ -1329,23 +771,86 @@ static void wcmDevClose(InputInfoPtr pInfo)
}
}
}
-
+
+static void wcmEnableDisableTool(DeviceIntPtr dev, Bool enable)
+{
+ InputInfoPtr pInfo = dev->public.devicePrivate;
+ WacomDevicePtr priv = pInfo->private;
+ WacomToolPtr tool = priv->tool;
+
+ tool->enabled = enable;
+}
+
+static void wcmEnableTool(DeviceIntPtr dev)
+{
+ wcmEnableDisableTool(dev, TRUE);
+}
+static void wcmDisableTool(DeviceIntPtr dev)
+{
+ wcmEnableDisableTool(dev, FALSE);
+}
+
+/**
+ * Unlink the touch tool from the pen of the same device
+ */
+static void wcmUnlinkTouchAndPen(InputInfoPtr pInfo)
+{
+ WacomDevicePtr priv = pInfo->private;
+ WacomCommonPtr common = priv->common;
+ InputInfoPtr device = xf86FirstLocalDevice();
+ WacomCommonPtr tmpcommon = NULL;
+ WacomDevicePtr tmppriv = NULL;
+ Bool touch_device = FALSE;
+
+ if (!TabletHasFeature(common, WCM_PENTOUCH))
+ return;
+
+ /* Lookup to find the associated pen and touch */
+ for (; device != NULL; device = device->next)
+ {
+ if (!strcmp(device->drv->driverName, "wacom"))
+ {
+ tmppriv = (WacomDevicePtr) device->private;
+ tmpcommon = tmppriv->common;
+ touch_device = (common->wcmTouchDevice ||
+ tmpcommon->wcmTouchDevice);
+
+ /* skip the same tool or unlinked devices */
+ if ((tmppriv == priv) || !touch_device)
+ continue;
+
+ if (tmpcommon->tablet_id == common->tablet_id)
+ {
+ common->wcmTouchDevice = NULL;
+ tmpcommon->wcmTouchDevice = NULL;
+ common->tablet_type &= ~WCM_PENTOUCH;
+ tmpcommon->tablet_type &= ~WCM_PENTOUCH;
+ return;
+ }
+ }
+ }
+}
+
/*****************************************************************************
* wcmDevProc --
- * Handle the initialization, etc. of a wacom
+ * Handle the initialization, etc. of a wacom tablet. Called by the server
+ * once with DEVICE_INIT when the device becomes available, then
+ * DEVICE_ON/DEVICE_OFF possibly multiple times as the device is enabled
+ * and disabled. DEVICE_CLOSE is called before removal of the device.
****************************************************************************/
static int wcmDevProc(DeviceIntPtr pWcm, int what)
{
InputInfoPtr pInfo = (InputInfoPtr)pWcm->public.devicePrivate;
+#ifdef DEBUG
WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
+#endif
+ Status rc = !Success;
DBG(2, priv, "BEGIN dev=%p priv=%p "
"type=%s flags=%d fd=%d what=%s\n",
(void *)pWcm, (void *)priv,
- IsStylus(priv) ? "stylus" :
- IsCursor(priv) ? "cursor" :
- IsPad(priv) ? "pad" : "eraser",
+ pInfo->type_name,
priv->flags, pInfo ? pInfo->fd : -1,
(what == DEVICE_INIT) ? "INIT" :
(what == DEVICE_OFF) ? "OFF" :
@@ -1354,51 +859,43 @@ static int wcmDevProc(DeviceIntPtr pWcm, int what)
switch (what)
{
- /* All devices must be opened here to initialize and
- * register even a 'pad' which doesn't "SendCoreEvents"
- */
case DEVICE_INIT:
- priv->wcmDevOpenCount = 0;
- priv->wcmInitKeyClassCount = 0;
- if (!wcmDevOpen(pWcm))
- {
- DBG(1, priv, "INIT FAILED\n");
- return !Success;
- }
- priv->wcmInitKeyClassCount++;
- priv->wcmDevOpenCount++;
- break;
+ if (!wcmDevInit(pWcm))
+ goto out;
+ break;
case DEVICE_ON:
if (!wcmDevOpen(pWcm))
- {
- DBG(1, priv, "ON FAILED\n");
- return !Success;
- }
- priv->wcmDevOpenCount++;
+ goto out;
+ wcmEnableTool(pWcm);
xf86AddEnabledDevice(pInfo);
pWcm->public.on = TRUE;
break;
case DEVICE_OFF:
case DEVICE_CLOSE:
+ wcmDisableTool(pWcm);
+ wcmUnlinkTouchAndPen(pInfo);
if (pInfo->fd >= 0)
{
xf86RemoveEnabledDevice(pInfo);
wcmDevClose(pInfo);
}
pWcm->public.on = FALSE;
- priv->wcmDevOpenCount = 0;
break;
default:
- xf86Msg(X_ERROR, "%s: wacom unsupported mode=%d\n", pInfo->name, what);
- return !Success;
- break;
+ xf86Msg(X_ERROR, "%s: invalid mode=%d. This is an X server bug.\n",
+ pInfo->name, what);
+ goto out;
} /* end switch */
- DBG(2, priv, "END Success \n");
- return Success;
+ rc = Success;
+
+out:
+ if (rc != Success)
+ DBG(1, priv, "Failed during %d\n", what);
+ return rc;
}
-/* vim: set noexpandtab shiftwidth=8: */
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/xf86Wacom.h b/src/xf86Wacom.h
index 2db752d..fc1b4f4 100644
--- a/src/xf86Wacom.h
+++ b/src/xf86Wacom.h
@@ -23,12 +23,9 @@
#include <xorg-server.h>
#include <xorgVersion.h>
+#include <wacom-util.h>
#include "Xwacom.h"
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
-
-#define MAX_USB_EVENTS 32
-
/* max number of input events to read in one read call */
#define MAX_EVENTS 50
@@ -39,9 +36,12 @@
#include <xf86Xinput.h>
#include <mipointer.h>
-
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
-# include <X11/Xatom.h>
+#include <X11/Xatom.h>
+/*****************************************************************************
+ * Unit test hack
+ ****************************************************************************/
+#ifdef DISABLE_STATIC
+#define static
#endif
/******************************************************************************
@@ -56,8 +56,8 @@
#define DBG(lvl, priv, ...) \
do { \
if ((lvl) <= priv->debugLevel) { \
- xf86Msg(X_INFO, "%s (%s): ", \
- ((WacomDeviceRec*)priv)->name, __func__); \
+ xf86Msg(X_INFO, "%s (%d:%s): ", \
+ ((WacomDeviceRec*)priv)->name, lvl, __func__); \
xf86Msg(X_NONE, __VA_ARGS__); \
} \
} while (0)
@@ -65,21 +65,6 @@
#define DBG(lvl, priv, ...)
#endif
-/*****************************************************************************
- * General Macros
- ****************************************************************************/
-
-#define ABS(x) ((x) > 0 ? (x) : -(x))
-
-/*****************************************************************************
- * General Defines
- ****************************************************************************/
-#define XI_STYLUS "STYLUS" /* X device name for the stylus */
-#define XI_CURSOR "CURSOR" /* X device name for the cursor */
-#define XI_ERASER "ERASER" /* X device name for the eraser */
-#define XI_PAD "PAD" /* X device name for the Pad */
-#define XI_TOUCH "TOUCH" /* X device name for the touch */
-
/******************************************************************************
* WacomModule - all globals are packed in a single structure to keep the
* global namespaces as clean as possible.
@@ -118,6 +103,9 @@ struct _WacomModule
*/
#define SYSCALL(call) while(((call) == -1) && (errno == EINTR))
+/* Open the device with the right serial parmeters */
+extern Bool wcmOpen(InputInfoPtr pInfo);
+
/* device autoprobing */
char *wcmEventAutoDevProbe (InputInfoPtr pInfo);
@@ -148,11 +136,12 @@ extern int wcmDeviceTypeKeys(InputInfoPtr pInfo);
/* hotplug */
extern int wcmNeedAutoHotplug(InputInfoPtr pInfo, const char **type);
-extern void wcmHotplugOthers(InputInfoPtr pInfo);
-extern int wcmAutoProbeDevice(InputInfoPtr pInfo);
+extern void wcmHotplugOthers(InputInfoPtr pInfo, const char *basename);
/* setup */
-extern int wcmParseOptions(InputInfoPtr pInfo);
+extern Bool wcmPreInitParseOptions(InputInfoPtr pInfo, Bool is_primary, Bool is_dependent);
+extern Bool wcmPostInitParseOptions(InputInfoPtr pInfo, Bool is_primary, Bool is_dependent);
+extern int wcmParseSerials(InputInfoPtr pinfo);
extern void wcmInitialCoordinates(InputInfoPtr pInfo, int axes);
extern void wcmInitialScreens(InputInfoPtr pInfo);
extern void wcmInitialScreens(InputInfoPtr pInfo);
@@ -162,27 +151,40 @@ extern int wcmDevSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode);
/* run-time modifications */
extern void wcmChangeScreen(InputInfoPtr pInfo, int value);
-extern void wcmTilt2R(WacomDeviceStatePtr ds);
-extern void wcmFingerTapToClick(WacomCommonPtr common);
+extern int wcmTilt2R(int x, int y, double offset);
extern void wcmEmitKeycode(DeviceIntPtr keydev, int keycode, int state);
+extern void wcmSoftOutEvent(InputInfoPtr pInfo);
extern void wcmRotateTablet(InputInfoPtr pInfo, int value);
-extern void wcmRotateCoordinates(InputInfoPtr pInfo, int* x, int* y);
+extern void wcmRotateAndScaleCoordinates(InputInfoPtr pInfo, int* x, int* y);
extern void wcmVirtualTabletSize(InputInfoPtr pInfo);
extern void wcmVirtualTabletPadding(InputInfoPtr pInfo);
-extern void wcmAdjustArea(InputInfoPtr pInfo, WacomToolAreaPtr area);
extern int wcmCheckPressureCurveValues(int x0, int y0, int x1, int y1);
+extern int wcmGetPhyDeviceID(WacomDevicePtr priv);
/* device properties */
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
extern int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, BOOL checkonly);
+extern int wcmGetProperty(DeviceIntPtr dev, Atom property);
+extern int wcmDeleteProperty(DeviceIntPtr dev, Atom property);
extern void InitWcmDeviceProperties(InputInfoPtr pInfo);
-#endif
-
-/* Device probing */
-int isdv4ProbeKeys(InputInfoPtr pInfo);
-int usbProbeKeys(InputInfoPtr pInfo);
+extern void wcmUpdateRotationProperty(WacomDevicePtr priv);
+extern void wcmUpdateSerial(InputInfoPtr pInfo, unsigned int serial);
+
+/* Utility functions */
+extern Bool is_absolute(InputInfoPtr pInfo);
+extern void set_absolute(InputInfoPtr pInfo, Bool absolute);
+extern WacomCommonPtr wcmRefCommon(WacomCommonPtr common);
+extern void wcmFreeCommon(WacomCommonPtr *common);
+extern WacomCommonPtr wcmNewCommon(void);
+
+enum WacomSuppressMode {
+ SUPPRESS_NONE = 8, /* Process event normally */
+ SUPPRESS_ALL, /* Supress and discard the whole event */
+ SUPPRESS_NON_MOTION /* Supress all events but x/y motion */
+};
/****************************************************************************/
#endif /* __XF86WACOM_H */
+
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/src/xf86WacomDefs.h b/src/xf86WacomDefs.h
index f265894..2f3f7b4 100644
--- a/src/xf86WacomDefs.h
+++ b/src/xf86WacomDefs.h
@@ -23,15 +23,31 @@
/*****************************************************************************
* General Defines
****************************************************************************/
+#include <wacom-util.h>
#include <asm/types.h>
#include <linux/input.h>
#define MAX_USB_EVENTS 32
-#define WACOM_VENDOR_ID 0x056a /* vendor ID on the kernel device */
+
+/* vendor IDs on the kernel device */
+#define WACOM_VENDOR_ID 0x056a
+#define WALTOP_VENDOR_ID 0x172F
+#define NTRIG_VENDOR_ID 0x1b96
+#define HANWANG_VENDOR_ID 0x0b57
+#define LENOVO_VENDOR_ID 0x17ef
#define DEFAULT_SUPPRESS 2 /* default suppress */
#define MAX_SUPPRESS 100 /* max value of suppress */
#define BUFFER_SIZE 256 /* size of reception buffer */
#define MAXTRY 3 /* max number of try to receive magic number */
+#define MIN_ROTATION -900 /* the minimum value of the marker pen rotation */
+#define MAX_ROTATION_RANGE 1800 /* the maximum range of the marker pen rotation */
+#define MAX_ABS_WHEEL 1023 /* the maximum value of absolute wheel */
+
+#define MIN_PAD_RING 0 /* I4 absolute scroll ring min value */
+#define MAX_PAD_RING 71 /* I4 absolute scroll ring max value */
+
+/* I4 cursor tool has a rotation offset of 175 degrees */
+#define INTUOS4_CURSOR_ROTATION_OFFSET 175
/* Default max distance to the tablet at which a proximity-out event is generated for
* cursor device (e.g. mouse).
@@ -39,22 +55,46 @@
#define PROXOUT_INTUOS_DISTANCE 10
#define PROXOUT_GRAPHIRE_DISTANCE 42
+/* 2.6.28 */
+
#ifndef BTN_TOOL_DOUBLETAP
#define BTN_TOOL_DOUBLETAP 0x14d
#endif
+#ifndef BTN_TOOL_TRIPLETAP
+#define BTN_TOOL_TRIPLETAP 0x14e
+#endif
+
+/* 2.6.30 */
+
+#ifndef ABS_MT_POSITION_X
+#define ABS_MT_POSITION_X 0x35
+#endif
+
+#ifndef ABS_MT_POSITION_Y
+#define ABS_MT_POSITION_Y 0x36
+#endif
+
+#ifndef ABS_MT_TRACKING_ID
+#define ABS_MT_TRACKING_ID 0x39
+#endif
+
+/* 2.6.33 */
+
+#ifndef ABS_MT_PRESSURE
+#define ABS_MT_PRESSURE 0x3a
+#endif
+
+/* 2.6.36 */
+
+#ifndef ABS_MT_SLOT
+#define ABS_MT_SLOT 0x2f
+#endif
+
/* defines to discriminate second side button and the eraser */
#define ERASER_PROX 4
#define OTHER_PROX 1
-/* to access kernel defined bits */
-#define BIT(x) (1<<((x) & (BITS_PER_LONG - 1)))
-#define BITS_PER_LONG (sizeof(long) * 8)
-#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
-#define ISBITSET(x,y) ((x)[LONG(y)] & BIT(y))
-#define OFF(x) ((x)%BITS_PER_LONG)
-#define LONG(x) ((x)/BITS_PER_LONG)
-
/******************************************************************************
* Forward Declarations
*****************************************************************************/
@@ -82,8 +122,6 @@ struct _WacomModel
int (*GetRanges)(InputInfoPtr pInfo);
int (*Start)(InputInfoPtr pInfo);
int (*Parse)(InputInfoPtr pInfo, const unsigned char* data, int len);
- int (*FilterRaw)(WacomCommonPtr common, WacomChannelPtr pChannel,
- WacomDeviceStatePtr ds);
int (*DetectConfig)(InputInfoPtr pInfo);
};
@@ -91,6 +129,7 @@ struct _WacomModel
* WacomDeviceRec
*****************************************************************************/
+/* these device IDs are reported through ABS_MISC for Protocol 4 devices */
#define DEVICE_ID(flags) ((flags) & 0xff)
#define STYLUS_DEVICE_ID 0x02
#define TOUCH_DEVICE_ID 0x03
@@ -104,31 +143,57 @@ struct _WacomModel
#define ERASER_ID 0x00000008
#define PAD_ID 0x00000010
+/* Each tablet may have one or more of the following
+ * features */
+#define WCM_PEN 0x00000001 /* Tablet supports pens */
+#define WCM_PAD 0x00000002 /* Has a pad tool */
+
+#define WCM_1FGT 0x00000004 /* One finger touch */
+#define WCM_2FGT 0x00000008 /* Two finger touch */
+#define WCM_STRIP 0x00000010 /* Tablet has menu strip (e.g.
+ Intuos3) */
+#define WCM_RING 0x00000020 /* Tablet has touch ring (e.g.
+ Intuos4) */
+#define WCM_DUALINPUT 0x00000040 /* Supports two tools on the
+ tablet simultaneously (Intuos
+ 1 and 2) */
+#define WCM_ROTATION 0x00000080 /* Needs to convert mouse tool
+ tilt to rotation */
+#define WCM_LCD 0x00000100 /* Cintiqs and other display
+ tablets */
+#define WCM_TPC (0x00000200 | WCM_LCD) /* TabletPC (special
+ button handling,
+ always an LCD) */
+#define WCM_PENTOUCH 0x00000400 /* Tablet supports pen and touch */
+#define WCM_DUALRING 0x00000800 /* Tablet has two touch rings */
+#define TabletHasFeature(common, feature) MaskIsSet((common)->tablet_type, (feature))
+#define TabletSetFeature(common, feature) MaskSet((common)->tablet_type, (feature))
+
#define ABSOLUTE_FLAG 0x00000100
-#define KEEP_SHAPE_FLAG 0x00000200
#define BAUD_19200_FLAG 0x00000400
#define BUTTONS_ONLY_FLAG 0x00000800
-#define TPCBUTTONS_FLAG 0x00001000
-#define TPCBUTTONONE_FLAG 0x00002000
-#define COREEVENT_FLAG 0x00004000
#define IsCursor(priv) (DEVICE_ID((priv)->flags) == CURSOR_ID)
#define IsStylus(priv) (DEVICE_ID((priv)->flags) == STYLUS_ID)
#define IsTouch(priv) (DEVICE_ID((priv)->flags) == TOUCH_ID)
#define IsEraser(priv) (DEVICE_ID((priv)->flags) == ERASER_ID)
#define IsPad(priv) (DEVICE_ID((priv)->flags) == PAD_ID)
+#define IsPen(priv) (IsStylus(priv) || IsEraser(priv))
+
+#define IsUSBDevice(common) ((common)->wcmDevCls == &gWacomUSBDevice)
#define FILTER_PRESSURE_RES 2048 /* maximum points in pressure curve */
#define WCM_MAX_BUTTONS 32 /* maximum number of tablet buttons */
#define WCM_MAX_MOUSE_BUTTONS 16 /* maximum number of buttons-on-pointer
* (which are treated as mouse buttons,
* not as keys like tablet menu buttons).
- * For backword compability support,
+ * For backward compability support,
* tablet buttons besides the strips are
* treated as buttons */
-/* get/set/property */
-#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
+#define AXIS_INVERT 0x01 /* Flag describing an axis which increases "downward" */
+#define AXIS_BITWISE 0x02 /* Flag describing an axis which changes bitwise */
+/* get/set/property */
typedef struct _PROPINFO PROPINFO;
struct _PROPINFO
@@ -140,95 +205,83 @@ struct _PROPINFO
int nSize;
int nDefault;
};
-#endif /* GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3 */
struct _WacomDeviceRec
{
- char *name; /* Do not move, same offset as common->wcmDevice */
+ char *name; /* Do not move, same offset as common->device_path. Used by DBG macro */
/* configuration fields */
struct _WacomDeviceRec *next;
InputInfoPtr pInfo;
int debugLevel;
unsigned int flags; /* various flags (type, abs, touch...) */
- int topX; /* X top in device coords*/
- int topY; /* Y top in device coords*/
- int bottomX; /* X bottom in device coords*/
- int bottomY; /* Y bottom in device coords*/
+ int topX; /* X top in device coordinates */
+ int topY; /* Y top in device coordinates */
+ int bottomX; /* X bottom in device coordinates */
+ int bottomY; /* Y bottom in device coordinates */
int resolX; /* X resolution */
int resolY; /* Y resolution */
- int maxX; /* tool logical maxX */
- int maxY; /* tool logical maxY */
- int sizeX; /* active X size in device coords */
- int sizeY; /* active Y size in device coords */
+ int maxX; /* tool physical maxX in device coordinates*/
+ int maxY; /* tool physical maxY in device coordinates*/
double factorX; /* X factor */
double factorY; /* Y factor */
- unsigned int serial; /* device serial number */
- int screen_no; /* associated screen */
- /* The next 4 are set from the TVResolution coordinates if TwinView
- * is active */
- int screenTopX[32]; /* left cordinate of the associated screen in screen coords */
- int screenTopY[32]; /* top cordinate of the associated screen in screen coords */
- int screenBottomX[32]; /* right cordinate of the associated screen in screen coords */
- int screenBottomY[32]; /* bottom cordinate of the associated screen in screen coords */
+ unsigned int serial; /* device serial number this device takes (if 0, any serial is ok) */
+ unsigned int cur_serial; /* current serial in prox */
int maxWidth; /* max active screen width in screen coords */
int maxHeight; /* max active screen height in screen coords */
- int leftPadding; /* left padding for virtual tablet in device coords */
- int topPadding; /* top padding for virtual in device coords tablet */
- int button[WCM_MAX_BUTTONS];/* buttons assignments */
- unsigned keys[WCM_MAX_BUTTONS][256]; /* keystrokes assigned to buttons */
+ int leftPadding; /* left padding for virtual tablet in device coordinates*/
+ int topPadding; /* top padding for virtual tablet in device coordinates*/
+ /* map zero based internal buttons to one based X buttons */
+ int button[WCM_MAX_BUTTONS];
+ /* map one based X buttons to keystrokes */
+ unsigned keys[WCM_MAX_BUTTONS+1][256];
int relup;
- unsigned rupk[256]; /* keystrokes assigned to relative wheel up event (default is button 4) */
int reldn;
- unsigned rdnk[256]; /* keystrokes assigned to relative wheel down event (default is button 5) */
int wheelup;
- unsigned wupk[256]; /* keystrokes assigned to absolute wheel/throttle up event (default is button 4) */
int wheeldn;
- unsigned wdnk[256]; /* keystrokes assigned to absolute wheel/throttle down event (default is button 5) */
+ int wheel2up;
+ int wheel2dn;
+ /* keystrokes assigned to wheel events (default is the buttons above).
+ * Order is relup, reldwn, wheelup, wheeldn, wheel2up, wheel2dn.
+ * Like 'keys', this array is one-indexed */
+ unsigned wheel_keys[6+1][256];
+
int striplup;
- unsigned slupk[256]; /* keystrokes assigned to left strip up event (default is button 4) */
int stripldn;
- unsigned sldnk[256]; /* keystrokes assigned to left strip up event (default is button 5) */
int striprup;
- unsigned srupk[256]; /* keystrokes assigned to right strip up event (default is button 4) */
int striprdn;
- unsigned srdnk[256]; /* keystrokes assigned to right strip up event (default is button 4) */
+ /* keystrokes assigned to strip events (default is the buttons above).
+ * Order is striplup, stripldn, striprup, striprdn. Like 'keys', this
+ * array is one-indexed */
+ unsigned strip_keys[4+1][256];
int nbuttons; /* number of buttons for this subdevice */
int naxes; /* number of axes */
+ /* FIXME: always 6, and the code relies on that... */
WacomCommonPtr common; /* common info pointer */
- /* state fields */
+ /* state fields in device coordinates */
int currentX; /* current X position */
int currentY; /* current Y position */
- int currentSX; /* current screen X position */
- int currentSY; /* current screen Y position */
+ int currentSX; /* current screen X position in screen coords */
+ int currentSY; /* current screen Y position in screen coords */
int oldX; /* previous X position */
int oldY; /* previous Y position */
int oldZ; /* previous pressure */
- int oldCapacity; /* previous capacity */
int oldTiltX; /* previous tilt in x direction */
int oldTiltY; /* previous tilt in y direction */
int oldWheel; /* previous wheel value */
+ int oldWheel2; /* previous wheel2 value */
int oldRot; /* previous rotation value */
int oldStripX; /* previous left strip value */
int oldStripY; /* previous right strip value */
int oldThrottle; /* previous throttle value */
int oldButtons; /* previous buttons state */
int oldProximity; /* previous proximity */
- int hardProx; /* previous hardware proximity */
+ int oldCursorHwProx; /* previous cursor hardware proximity */
int old_device_id; /* last in prox device id */
- int old_serial; /* last in prox tool serial number */
+ unsigned int old_serial;/* last in prox tool serial number */
int devReverseCount; /* Relative ReverseConvert called twice each movement*/
- int numScreen; /* number of configured screens */
- int currentScreen; /* current screen in display */
- int twinview; /* using twinview mode of gfx card */
- int tvoffsetX; /* X edge offset for TwinView setup in device coords */
- int tvoffsetY; /* Y edge offset for TwinView setup in device coords */
- int tvResolution[4]; /* twinview screens' resultion */
- int wcmMMonitor; /* disable/enable moving across screens in multi-monitor desktop */
- int wcmDevOpenCount; /* Device open count */
- int wcmInitKeyClassCount; /* Device InitKeyClassDeviceStruct count */
/* JEJ - throttle */
int throttleStart; /* time in ticks for last wheel movement */
@@ -236,14 +289,20 @@ struct _WacomDeviceRec
int throttleValue; /* current throttle value */
/* JEJ - filters */
- int* pPressCurve; /* pressure curve */
+ int pPressCurve[FILTER_PRESSURE_RES + 1]; /* pressure curve */
int nPressCtrl[4]; /* control points for curve */
+ int minPressure; /* the minimum pressure a pen may hold */
WacomToolPtr tool; /* The common tool-structure for this device */
- WacomToolAreaPtr toolarea; /* The area defined for this device */
int isParent; /* set to 1 if the device is not auto-hotplugged */
- Atom btn_actions[WCM_MAX_BUTTONS]; /* property handlers to listen to */
+
+ /* property handlers to listen to for action properties */
+ Atom btn_actions[WCM_MAX_BUTTONS];
+ Atom wheel_actions[4];
+ Atom strip_actions[4];
+
+ OsTimerPtr serial_timer; /* timer used for serial number property update */
};
/******************************************************************************
@@ -263,17 +322,16 @@ struct _WacomDeviceState
int y;
int buttons;
int pressure;
- int capacity;
int tiltx;
int tilty;
int stripx;
int stripy;
int rotation;
int abswheel;
+ int abswheel2;
int relwheel;
int distance;
int throttle;
- int discard_first;
int proximity;
int sample; /* wraps every 24 days */
};
@@ -285,8 +343,6 @@ struct _WacomFilterState
int y[MAX_SAMPLES];
int tiltx[MAX_SAMPLES];
int tilty[MAX_SAMPLES];
- int statex;
- int statey;
};
struct _WacomChannel
@@ -318,11 +374,13 @@ struct _WacomChannel
struct _WacomDeviceClass
{
Bool (*Detect)(InputInfoPtr pInfo); /* detect device */
+ Bool (*ParseOptions)(InputInfoPtr pInfo); /* parse class-specific options */
Bool (*Init)(InputInfoPtr pInfo, char* id, float *version); /* initialize device */
+ int (*ProbeKeys)(InputInfoPtr pInfo); /* set the bits for the keys supported */
};
- extern WacomDeviceClass gWacomUSBDevice;
- extern WacomDeviceClass gWacomISDV4Device;
+extern WacomDeviceClass gWacomUSBDevice;
+extern WacomDeviceClass gWacomISDV4Device;
/******************************************************************************
* WacomCommonRec
@@ -330,29 +388,42 @@ struct _WacomDeviceClass
#define TILT_REQUEST_FLAG 1
#define TILT_ENABLED_FLAG 2
-#define RAW_FILTERING_FLAG 4
-/* set if the /dev/input driver should wait for SYN_REPORT events as the
- * end of record indicator or not
-*/
-#define USE_SYN_REPORTS_FLAG 8
-#define AUTODEV_FLAG 16
-
-#define DEVICE_ISDV4 0x000C
-#define MAX_CHANNELS 2
+#define MAX_CHANNELS 3
+#define PAD_CHANNEL (MAX_CHANNELS-1)
#define MAX_FINGERS 2
+typedef struct {
+ int wcmZoomDistance; /* minimum distance for a zoom touch gesture */
+ int wcmScrollDistance; /* minimum motion before sending a scroll gesture */
+ int wcmScrollDirection; /* store the vertical or horizontal bit in use */
+ int wcmMaxScrollFingerSpread; /* maximum distance between fingers for scroll gesture */
+ int wcmGestureUsed; /* retain used gesture count within one in-prox event */
+ int wcmTapTime; /* minimum time between taps for a right click */
+} WacomGesturesParameters;
+
+enum WacomProtocol {
+ WCM_PROTOCOL_GENERIC,
+ WCM_PROTOCOL_4,
+ WCM_PROTOCOL_5
+};
+
struct _WacomCommonRec
{
- /* Do not move wcmDevice, same offset as priv->name */
- char* wcmDevice; /* device file name */
+ /* Do not move device_path, same offset as priv->name. Used by DBG macro */
+ char* device_path; /* device file name */
dev_t min_maj; /* minor/major number */
unsigned char wcmFlags; /* various flags (handle tilt) */
int debugLevel;
+ int vendor_id; /* Vendor ID */
int tablet_id; /* USB tablet ID */
+ int tablet_type; /* bitmask of tablet features (WCM_LCD, WCM_PEN, etc) */
int fd; /* file descriptor to tablet */
int fd_refs; /* number of references to fd; if =0, fd is invalid */
unsigned long wcmKeys[NBITS(KEY_MAX)]; /* supported tool types for the device */
+ WacomDevicePtr wcmTouchDevice; /* The pointer for pen to access the
+ touch tool of the same device id */
+ Bool wcmPenInProx; /* Keep pen in-prox state for touch tool */
/* These values are in tablet coordinates */
int wcmMaxX; /* tablet max X value */
@@ -360,13 +431,12 @@ struct _WacomCommonRec
int wcmMaxZ; /* tablet max Z value */
int wcmMaxTouchX; /* touch panel max X value */
int wcmMaxTouchY; /* touch panel max Y value */
- int wcmResolX; /* pen tool X resolution in points/inch */
- int wcmResolY; /* pen tool Y resolution in points/inch */
- int wcmTouchResolX; /* touch X resolution in points/mm */
- int wcmTouchResolY; /* touch Y resolution in points/mm */
+ int wcmResolX; /* pen tool X resolution in points/m */
+ int wcmResolY; /* pen tool Y resolution in points/m */
+ int wcmTouchResolX; /* touch X resolution in points/m */
+ int wcmTouchResolY; /* touch Y resolution in points/m */
/* tablet Z resolution is equivalent
* to wcmMaxZ which is equal to 100% pressure */
- int wcmMaxCapacity; /* max capacity value */
int wcmMaxDist; /* tablet max distance value */
int wcmMaxtiltX; /* styli max tilt in X directory */
int wcmMaxtiltY; /* styli max tilt in Y directory */
@@ -375,52 +445,46 @@ struct _WacomCommonRec
int wcmMaxStripY; /* Maximum fingerstrip Y */
int nbuttons; /* total number of buttons */
- int npadkeys; /* number of pad keys in the above array */
int padkey_code[WCM_MAX_BUTTONS];/* hardware codes for buttons */
+ int npadkeys; /* number of pad keys in the above array */
WacomDevicePtr wcmDevices; /* list of devices sharing same port */
int wcmPktLength; /* length of a packet */
- int wcmProtocolLevel; /* 4 for Wacom IV, 5 for Wacom V */
+ int wcmProtocolLevel; /* Wacom Protocol used */
float wcmVersion; /* ROM version */
- int wcmForceDevice; /* force device type (used by ISD V4) */
int wcmRotate; /* rotate screen (for TabletPC) */
int wcmThreshold; /* Threshold for button pressure */
WacomChannel wcmChannel[MAX_CHANNELS]; /* channel device state */
- unsigned int wcmISDV4Speed; /* serial ISDV4 link speed */
WacomDeviceClassPtr wcmDevCls; /* device class functions */
WacomModelPtr wcmModel; /* model-specific functions */
int wcmTPCButton; /* set Tablet PC button on/off */
int wcmTouch; /* disable/enable touch event */
- int wcmTPCButtonDefault; /* Tablet PC button default */
int wcmTouchDefault; /* default to disable when not supported */
int wcmGesture; /* disable/enable touch gesture */
int wcmGestureDefault; /* default touch gesture to disable when not supported */
int wcmGestureMode; /* data is in Gesture Mode? */
WacomDeviceState wcmGestureState[MAX_FINGERS]; /* inital state when in gesture mode */
- int wcmCapacity; /* disable/enable capacity */
- int wcmCapacityDefault; /* default to -1 when capacity isn't supported/disabled */
- /* 3 when capacity is supported */
+ WacomGesturesParameters wcmGestureParameters;
int wcmMaxCursorDist; /* Max mouse distance reported so far */
int wcmCursorProxoutDist; /* Max mouse distance for proxy-out max/256 units */
int wcmCursorProxoutDistDefault; /* Default max mouse distance for proxy-out */
int wcmSuppress; /* transmit position on delta > supress */
int wcmRawSample; /* Number of raw data used to filter an event */
- int wcmScaling; /* dealing with missing calling DevConvert case. Default 0 */
int bufpos; /* position with buffer */
unsigned char buffer[BUFFER_SIZE]; /* data read from device */
- int wcmLastToolSerial;
- int wcmEventCnt;
- struct input_event wcmEvents[MAX_USB_EVENTS]; /* events for current change */
+ void *private; /* backend-specific information */
WacomToolPtr wcmTool; /* List of unique tools */
+ WacomToolPtr serials; /* Serial numbers provided at startup*/
+
+ /* DO NOT TOUCH THIS. use wcmRefCommon() instead */
+ int refcnt; /* number of devices sharing this struct */
};
#define HANDLE_TILT(comm) ((comm)->wcmFlags & TILT_ENABLED_FLAG)
-#define RAW_FILTERING(comm) ((comm)->wcmFlags & RAW_FILTERING_FLAG)
-#define USE_SYN_REPORTS(comm) ((comm)->wcmFlags & USE_SYN_REPORTS_FLAG)
/******************************************************************************
* WacomTool
@@ -430,25 +494,13 @@ struct _WacomTool
WacomToolPtr next; /* Next tool in list */
int typeid; /* Tool type */
- int serial; /* Serial id, 0 == no serial id */
-
- WacomToolAreaPtr current; /* Current area in-prox */
- WacomToolAreaPtr arealist; /* List of defined areas */
-};
-
-/******************************************************************************
- * WacomToolArea
- *****************************************************************************/
-struct _WacomToolArea
-{
- WacomToolAreaPtr next;
+ unsigned int serial; /* Serial id, 0 == no serial id */
+ Bool enabled;
+ char *name;
- int topX; /* Top X/Y */
- int topY;
- int bottomX; /* Bottom X/Y */
- int bottomY;
-
- InputInfoPtr device; /* The InputDevice connected to this area */
+ InputInfoPtr device; /* The InputDevice connected to this tool */
};
#endif /*__XF86_XF86WACOMDEFS_H */
+
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 0000000..8011ddb
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,19 @@
+if UNITTESTS
+include ../src/common.mk
+
+check_PROGRAMS = wacom-tests
+check_LTLIBRARIES = libwacom-test.la
+libwacom_test_la_SOURCES =$(DRIVER_SOURCES)
+libwacom_test_la_CFLAGS = -DDISABLE_STATIC -I$(top_srcdir)/src $(XORG_CFLAGS) $(CWARNFLAGS) -fvisibility=default
+
+TESTS=$(check_PROGRAMS)
+
+INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/include
+AM_CFLAGS = $(XORG_CFLAGS) $(CWARNFLAGS) $(UDEV_CFLAGS)
+TEST_LDADD= libwacom-test.la
+COMMON_SOURCES=fake-symbols.c fake-symbols.h
+
+wacom_tests_LDADD=$(TEST_LDADD) $(UDEV_LIBS)
+wacom_tests_SOURCES=wacom-tests.c $(COMMON_SOURCES)
+
+endif
diff --git a/test/fake-symbols.c b/test/fake-symbols.c
new file mode 100644
index 0000000..9607bf3
--- /dev/null
+++ b/test/fake-symbols.c
@@ -0,0 +1,498 @@
+#include "fake-symbols.h"
+
+_X_EXPORT
+int xf86ReadSerial (int fd, void *buf, int count)
+{
+ return 0;
+}
+
+
+_X_EXPORT int
+xf86WriteSerial (int fd, const void *buf, int count)
+{
+ return 0;
+}
+
+_X_EXPORT int
+xf86CloseSerial (int fd)
+{
+ return 0;
+}
+
+_X_EXPORT int
+xf86WaitForInput (int fd, int timeout)
+{
+ return 0;
+}
+
+_X_EXPORT int
+xf86OpenSerial (pointer options)
+{
+ return 0;
+}
+
+_X_EXPORT int
+xf86SetSerialSpeed (int fd, int speed)
+{
+ return 0;
+}
+
+_X_EXPORT OPTTYPE
+xf86ReplaceIntOption(OPTTYPE optlist, const char *name, const int val)
+{
+ return NULL;
+}
+
+_X_EXPORT char *
+xf86SetStrOption(OPTTYPE optlist, const char *name, CONST char *deflt)
+{
+ return NULL;
+}
+
+_X_EXPORT int
+xf86SetBoolOption(OPTTYPE optlist, const char *name, int deflt)
+{
+ return 0;
+}
+
+_X_EXPORT OPTTYPE
+xf86AddNewOption(OPTTYPE head, const char *name, const char *val)
+{
+ return NULL;
+}
+_X_EXPORT CONST char *
+xf86FindOptionValue(OPTTYPE options, const char *name)
+{
+ return NULL;
+}
+
+_X_EXPORT char *
+xf86OptionName(OPTTYPE opt)
+{
+ return NULL;
+}
+
+_X_EXPORT char *
+xf86OptionValue(OPTTYPE opt)
+{
+ return NULL;
+}
+
+_X_EXPORT int
+xf86NameCmp(const char *s1, const char *s2)
+{
+ return strcasecmp(s1, s2);
+}
+
+_X_EXPORT char *
+xf86CheckStrOption(OPTTYPE optlist, const char *name, char *deflt)
+{
+ return NULL;
+}
+
+_X_EXPORT void
+xf86AddEnabledDevice(InputInfoPtr pInfo)
+{
+ return;
+}
+
+_X_EXPORT void
+xf86RemoveEnabledDevice(InputInfoPtr pInfo)
+{
+ return;
+}
+
+_X_EXPORT Atom
+XIGetKnownProperty(char *name)
+{
+ return None;
+}
+
+_X_EXPORT void
+xf86AddInputDriver(InputDriverPtr driver, pointer module, int flags)
+{
+ return;
+}
+
+_X_EXPORT int
+xf86ScaleAxis(int Cx,
+ int to_max,
+ int to_min,
+ int from_max,
+ int from_min )
+{
+ int X;
+ int64_t to_width = to_max - to_min;
+ int64_t from_width = from_max - from_min;
+
+ if (from_width) {
+ X = (int)(((to_width * (Cx - from_min)) / from_width) + to_min);
+ }
+ else {
+ X = 0;
+ /*ErrorF ("Divide by Zero in xf86ScaleAxis\n");*/
+ }
+
+ if (X > to_max)
+ X = to_max;
+ if (X < to_min)
+ X = to_min;
+
+ return X;
+}
+
+
+_X_EXPORT void
+DeleteInputDeviceRequest(DeviceIntPtr pDev)
+{
+ return;
+}
+
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+_X_EXPORT void
+FreeInputAttributes(InputAttributes *attrs)
+{
+ return;
+}
+#endif
+
+_X_EXPORT void
+xf86PostButtonEvent(DeviceIntPtr device,
+ int is_absolute,
+ int button,
+ int is_down,
+ int first_valuator,
+ int num_valuators,
+ ...)
+{
+ return;
+}
+
+_X_EXPORT int
+Xasprintf(char ** ret, const char * format, ...)
+{
+ return 0;
+}
+
+
+_X_EXPORT int
+XISetDevicePropertyDeletable(DeviceIntPtr dev, Atom property, Bool deletable)
+{
+ return 0;
+}
+
+
+_X_EXPORT InputInfoPtr
+xf86FirstLocalDevice(void)
+{
+ return NULL;
+}
+
+
+_X_EXPORT void
+xf86DeleteInput(InputInfoPtr pInp, int flags)
+{
+ return;
+}
+
+_X_EXPORT OPTTYPE
+xf86OptionListDuplicate(OPTTYPE options)
+{
+ return NULL;
+}
+
+_X_EXPORT Bool
+InitButtonClassDeviceStruct(DeviceIntPtr dev, int numButtons, Atom* labels,
+ CARD8 *map)
+{
+ return FALSE;
+}
+
+_X_EXPORT void
+InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval, int maxval,
+ int resolution, int min_res, int max_res, int mode)
+{
+ return;
+}
+
+_X_EXPORT void
+xf86PostKeyboardEvent(DeviceIntPtr device,
+ unsigned int key_code,
+ int is_down)
+{
+ return;
+}
+
+_X_EXPORT int
+xf86SetIntOption(OPTTYPE optlist, const char *name, int deflt)
+{
+ return 0;
+}
+
+_X_EXPORT void
+xf86PostButtonEventP(DeviceIntPtr device,
+ int is_absolute,
+ int button,
+ int is_down,
+ int first_valuator,
+ int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators)
+{
+ return;
+}
+
+_X_EXPORT Bool
+InitPtrFeedbackClassDeviceStruct(DeviceIntPtr dev, PtrCtrlProcPtr controlProc)
+{
+ return FALSE;
+}
+
+_X_EXPORT int
+XIChangeDeviceProperty (DeviceIntPtr dev, Atom property, Atom type,
+ int format, int mode, unsigned long len,
+ pointer value, Bool sendevent)
+{
+ return 0;
+}
+
+_X_EXPORT CARD32
+GetTimeInMillis (void)
+{
+ return 0;
+}
+
+
+_X_EXPORT int
+NewInputDeviceRequest (InputOption *options,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+ InputAttributes *attrs,
+#endif
+ DeviceIntPtr *pdev)
+{
+ return 0;
+}
+
+
+_X_EXPORT Bool
+InitLedFeedbackClassDeviceStruct (DeviceIntPtr dev, LedCtrlProcPtr controlProc)
+{
+ return FALSE;
+}
+
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+_X_EXPORT InputAttributes*
+DuplicateInputAttributes(InputAttributes *attrs)
+{
+ return NULL;
+}
+#endif
+
+_X_EXPORT int
+ValidAtom(Atom atom)
+{
+ return None;
+}
+
+_X_EXPORT Bool
+InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet *rmlvo,
+ BellProcPtr bell_func, KbdCtrlProcPtr ctrl_func)
+{
+ return FALSE;
+}
+
+_X_EXPORT long
+XIRegisterPropertyHandler(DeviceIntPtr dev,
+ int (*SetProperty) (DeviceIntPtr dev,
+ Atom property,
+ XIPropertyValuePtr prop,
+ BOOL checkonly),
+ int (*GetProperty) (DeviceIntPtr dev,
+ Atom property),
+ int (*DeleteProperty) (DeviceIntPtr dev,
+ Atom property))
+{
+ return 0;
+}
+
+_X_EXPORT int
+InitProximityClassDeviceStruct(DeviceIntPtr dev)
+{
+ return 0;
+}
+
+
+_X_EXPORT void
+xf86Msg(MessageType type, const char *format, ...)
+{
+ return;
+}
+
+
+_X_EXPORT void
+xf86PostMotionEventP(DeviceIntPtr device,
+ int is_absolute,
+ int first_valuator,
+ int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators)
+{
+ return;
+}
+
+
+_X_EXPORT Bool
+InitValuatorClassDeviceStruct(DeviceIntPtr dev, int numAxes, Atom *labels,
+ int numMotionEvents, int mode)
+{
+ return FALSE;
+}
+
+
+_X_EXPORT OPTTYPE
+xf86ReplaceStrOption(OPTTYPE optlist, const char *name, const char* val)
+{
+ return NULL;
+}
+
+
+_X_EXPORT OPTTYPE
+xf86NextOption(OPTTYPE list)
+{
+ return NULL;
+}
+
+
+_X_EXPORT int
+XIGetDeviceProperty (DeviceIntPtr dev, Atom property, XIPropertyValuePtr *value)
+{
+ return 0;
+}
+
+
+_X_EXPORT Atom
+MakeAtom(const char *string, unsigned len, Bool makeit)
+{
+ return None;
+}
+
+
+_X_EXPORT int
+GetMotionHistorySize(void)
+{
+ return 0;
+}
+
+
+_X_EXPORT void
+xf86PostProximityEventP(DeviceIntPtr device,
+ int is_in,
+ int first_valuator,
+ int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators)
+{
+ return;
+}
+
+
+_X_EXPORT Bool
+InitFocusClassDeviceStruct(DeviceIntPtr dev)
+{
+ return FALSE;
+}
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12
+void
+xf86ProcessCommonOptions(InputInfoPtr pInfo, pointer list)
+{
+}
+
+void
+xf86CollectInputOptions(InputInfoPtr pInfo,
+ const char **defaultOpts,
+ pointer extraOpts)
+{
+}
+
+InputInfoPtr xf86AllocateInput(InputDriverPtr drv, int flags)
+{
+ return NULL;
+}
+
+#endif
+
+ClientPtr serverClient;
+
+Bool QueueWorkProc (
+ Bool (*function)(ClientPtr /* pClient */, pointer /* closure */),
+ ClientPtr client, pointer closure)
+{
+ return FALSE;
+}
+
+
+OsTimerPtr
+TimerSet(OsTimerPtr timer, int flags, CARD32 millis,
+ OsTimerCallback func, pointer arg)
+{
+ return NULL;
+}
+
+void TimerFree(OsTimerPtr timer)
+{
+}
+
+int
+xf86BlockSIGIO (void)
+{
+ return 0;
+}
+
+void
+xf86UnblockSIGIO (int wasset)
+{
+}
+
+/* This is not the same as the X server one, but it'll do for the tests */
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 14
+typedef struct _InputOption {
+ struct _InputOption *next;
+ char *key;
+ char *value;
+} InputOption;
+
+InputOption*
+input_option_new(InputOption *list, const char *key, const char *value)
+{
+ InputOption *new;
+
+ new = calloc(1, sizeof(InputOption));
+ new->key = strdup(key);
+ new->value = strdup(value);
+ new->next = list;
+ return new;
+}
+
+void
+input_option_free_list(InputOption **opts)
+{
+ InputOption *tmp = *opts;
+ while(*opts)
+ {
+ tmp = (*opts)->next;
+ free((*opts)->key);
+ free((*opts)->value);
+ free((*opts));
+ *opts = tmp;
+ }
+}
+#endif
diff --git a/test/fake-symbols.h b/test/fake-symbols.h
new file mode 100644
index 0000000..a145021
--- /dev/null
+++ b/test/fake-symbols.h
@@ -0,0 +1,193 @@
+#include <xorg-server.h>
+#include <xf86Xinput.h>
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 14
+#define OPTTYPE XF86OptionPtr
+#define CONST const
+#else
+#define OPTTYPE pointer
+#define CONST
+#endif
+
+extern int xf86ReadSerial (int fd, void *buf, int count);
+extern int xf86WriteSerial (int fd, const void *buf, int count);
+extern int xf86CloseSerial (int fd);
+extern int xf86WaitForInput (int fd, int timeout);
+extern int xf86OpenSerial (pointer options);
+extern int xf86SetSerialSpeed (int fd, int speed);
+
+extern OPTTYPE xf86ReplaceIntOption(OPTTYPE optlist, const char *name, const int val);
+extern OPTTYPE xf86AddNewOption(OPTTYPE head, const char *name, const char *val);
+extern char* xf86OptionName(OPTTYPE opt);
+extern CONST char* xf86FindOptionValue(OPTTYPE options, const char *name);
+extern int xf86NameCmp(const char *s1, const char *s2);
+extern char* xf86CheckStrOption(OPTTYPE optlist, const char *name, char *deflt);
+
+
+extern char * xf86SetStrOption(OPTTYPE optlist, const char *name, CONST char *deflt);
+extern int xf86SetBoolOption(OPTTYPE optlist, const char *name, int deflt);
+extern OPTTYPE xf86AddNewOption(OPTTYPE head, const char *name, const char *val);
+extern char* xf86OptionName(OPTTYPE opt);
+extern char *xf86OptionValue(OPTTYPE opt);
+extern int xf86NameCmp(const char *s1, const char *s2);
+extern char * xf86CheckStrOption(OPTTYPE optlist, const char *name, char *deflt);
+extern void xf86AddEnabledDevice(InputInfoPtr pInfo);
+extern void xf86RemoveEnabledDevice(InputInfoPtr pInfo);
+extern Atom XIGetKnownProperty(char *name);
+extern void xf86AddInputDriver(InputDriverPtr driver, pointer module, int flags);
+extern int
+xf86ScaleAxis(int Cx,
+ int to_max,
+ int to_min,
+ int from_max,
+ int from_min );
+
+extern void DeleteInputDeviceRequest(DeviceIntPtr pDev);
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+extern void FreeInputAttributes(InputAttributes *attrs);
+#endif
+extern void
+xf86PostButtonEvent(DeviceIntPtr device,
+ int is_absolute,
+ int button,
+ int is_down,
+ int first_valuator,
+ int num_valuators,
+ ...);
+extern int Xasprintf(char ** ret, const char *format, ...);
+extern int
+XISetDevicePropertyDeletable(DeviceIntPtr dev, Atom property, Bool deletable);
+
+extern InputInfoPtr xf86FirstLocalDevice(void);
+extern void xf86DeleteInput(InputInfoPtr pInp, int flags);
+extern OPTTYPE xf86OptionListDuplicate(OPTTYPE options);
+extern Bool
+InitButtonClassDeviceStruct(DeviceIntPtr dev, int numButtons, Atom* labels,
+ CARD8 *map);
+extern void
+InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval, int maxval,
+ int resolution, int min_res, int max_res, int mode);
+extern void
+xf86PostKeyboardEvent(DeviceIntPtr device,
+ unsigned int key_code,
+ int is_down);
+extern int
+xf86SetIntOption(OPTTYPE optlist, const char *name, int deflt);
+extern void
+xf86PostButtonEventP(DeviceIntPtr device,
+ int is_absolute,
+ int button,
+ int is_down,
+ int first_valuator,
+ int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators);
+extern Bool
+InitPtrFeedbackClassDeviceStruct(DeviceIntPtr dev, PtrCtrlProcPtr controlProc);
+
+extern int
+XIChangeDeviceProperty (DeviceIntPtr dev, Atom property, Atom type,
+ int format, int mode, unsigned long len,
+ pointer value, Bool sendevent);
+extern CARD32 GetTimeInMillis (void);
+
+extern int
+NewInputDeviceRequest (InputOption *options,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+ InputAttributes *attrs,
+#endif
+ DeviceIntPtr *pdev);
+
+extern Bool
+InitLedFeedbackClassDeviceStruct (DeviceIntPtr dev, LedCtrlProcPtr controlProc);
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 11
+extern InputAttributes* DuplicateInputAttributes(InputAttributes *attrs);
+#endif
+extern int ValidAtom(Atom atom);
+extern Bool
+InitKeyboardDeviceStruct(DeviceIntPtr dev, XkbRMLVOSet *rmlvo,
+ BellProcPtr bell_func, KbdCtrlProcPtr ctrl_func);
+extern long
+XIRegisterPropertyHandler(DeviceIntPtr dev,
+ int (*SetProperty) (DeviceIntPtr dev,
+ Atom property,
+ XIPropertyValuePtr prop,
+ BOOL checkonly),
+ int (*GetProperty) (DeviceIntPtr dev,
+ Atom property),
+ int (*DeleteProperty) (DeviceIntPtr dev,
+ Atom property));
+extern int InitProximityClassDeviceStruct(DeviceIntPtr dev);
+extern void xf86Msg(MessageType type, const char *format, ...);
+
+extern void
+xf86PostMotionEventP(DeviceIntPtr device,
+ int is_absolute,
+ int first_valuator,
+ int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators);
+
+extern Bool
+InitValuatorClassDeviceStruct(DeviceIntPtr dev, int numAxes, Atom *labels,
+ int numMotionEvents, int mode);
+
+extern OPTTYPE
+xf86ReplaceStrOption(OPTTYPE optlist, const char *name, const char* val);
+
+extern OPTTYPE xf86NextOption(OPTTYPE list);
+
+extern int
+XIGetDeviceProperty (DeviceIntPtr dev, Atom property, XIPropertyValuePtr *value);
+
+extern Atom MakeAtom(const char *string, unsigned len, Bool makeit);
+
+extern int GetMotionHistorySize(void);
+
+extern void
+xf86PostProximityEventP(DeviceIntPtr device,
+ int is_in,
+ int first_valuator,
+ int num_valuators,
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+ const
+#endif
+ int *valuators);
+
+extern Bool InitFocusClassDeviceStruct(DeviceIntPtr dev);
+
+
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12
+extern void
+xf86ProcessCommonOptions(InputInfoPtr pInfo, pointer list);
+
+extern void
+xf86CollectInputOptions(InputInfoPtr pInfo,
+ const char **defaultOpts,
+ pointer extraOpts);
+
+extern InputInfoPtr
+xf86AllocateInput(InputDriverPtr drv, int flags);
+
+
+extern ClientPtr serverClient;
+
+extern Bool QueueWorkProc (
+ Bool (*function)(ClientPtr /* pClient */, pointer /* closure */),
+ ClientPtr client, pointer closure);
+
+#endif
+
+extern OsTimerPtr
+TimerSet(OsTimerPtr timer, int flags, CARD32 millis,
+ OsTimerCallback func, pointer arg);
+
+extern void TimerFree(OsTimerPtr timer);
+
+extern int xf86BlockSIGIO (void);
+extern void xf86UnblockSIGIO (int wasset);
diff --git a/test/wacom-tests.c b/test/wacom-tests.c
new file mode 100644
index 0000000..a22c970
--- /dev/null
+++ b/test/wacom-tests.c
@@ -0,0 +1,642 @@
+/*
+ * Copyright 2011 © Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "fake-symbols.h"
+#include <xf86Wacom.h>
+
+/**
+ * NOTE: this file may not contain tests that require static variables. The
+ * current compiler undef static magic makes them local variables and thus
+ * change the behaviour.
+ */
+
+static void
+test_get_scroll_delta(void)
+{
+ int test_table[][5] = {
+ { 100, 25, 0, 0, 75}, { 25, 100, 0, 0, -75},
+ {-100, -25, 0, 0, -75}, {-25, -100, 0, 0, 75},
+ { 100, -25, 0, 0, 125}, {-25, 100, 0, 0,-125},
+ { 100, 100, 0, 0, 0}, {-25, -25, 0, 0, 0},
+
+ {23, 0, 50, 0, 23}, {0, 23, 50, 0, -23},
+ {24, 0, 50, 0, 24}, {0, 24, 50, 0, -24},
+ {25, 0, 50, 0, 25}, {0, 25, 50, 0, -25},
+ {26, 0, 50, 0, -25}, {0, 26, 50, 0, 25},
+ {27, 0, 50, 0, -24}, {0, 27, 50, 0, 24},
+ {28, 0, 50, 0, -23}, {0, 28, 50, 0, 23},
+
+ {1024, 0, 0, AXIS_BITWISE, 11}, {0, 1024, 0, AXIS_BITWISE, -11},
+
+ { 0, 4, 256, AXIS_BITWISE, -3}, {4, 0, 256, AXIS_BITWISE, 3},
+ { 1, 4, 256, AXIS_BITWISE, -2}, {4, 1, 256, AXIS_BITWISE, 2},
+ { 2, 4, 256, AXIS_BITWISE, -1}, {4, 2, 256, AXIS_BITWISE, 1},
+ { 4, 4, 256, AXIS_BITWISE, 0}, {4, 4, 256, AXIS_BITWISE, 0},
+ { 8, 4, 256, AXIS_BITWISE, 1}, {4, 8, 256, AXIS_BITWISE, -1},
+ { 16, 4, 256, AXIS_BITWISE, 2}, {4, 16, 256, AXIS_BITWISE, -2},
+ { 32, 4, 256, AXIS_BITWISE, 3}, {4, 32, 256, AXIS_BITWISE, -3},
+ { 64, 4, 256, AXIS_BITWISE, 4}, {4, 64, 256, AXIS_BITWISE, -4},
+ {128, 4, 256, AXIS_BITWISE, 5}, {4, 128, 256, AXIS_BITWISE, -5},
+ {256, 4, 256, AXIS_BITWISE, -4}, {4, 256, 256, AXIS_BITWISE, 4}
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(test_table); i++)
+ {
+ int delta;
+ int current, old, wrap, flags;
+ current = test_table[i][0];
+ old = test_table[i][1];
+ wrap = test_table[i][2];
+ flags = test_table[i][3];
+
+ delta = getScrollDelta(current, old, wrap, flags);
+ assert(delta == test_table[i][4]);
+
+ flags |= AXIS_INVERT;
+ delta = getScrollDelta(current, old, wrap, flags);
+ assert(delta == -1 * test_table[i][4]);
+ }
+}
+
+static void
+test_get_wheel_button(void)
+{
+ int delta;
+ int button_up, button_dn, action_up, action_dn;
+
+ button_up = 100;
+ button_dn = 200;
+ action_up = 300;
+ action_dn = 400;
+
+ for (delta = -32; delta <= 32; delta++)
+ {
+ int *action;
+ int result = getWheelButton(delta, button_up, button_dn, &action_up, &action_dn, &action);
+ if (delta < 0)
+ {
+ assert(result == button_dn);
+ assert(action == &action_dn);
+ }
+ else if (delta == 0)
+ {
+ assert(result == 0);
+ assert(action == NULL);
+ }
+ else
+ {
+ assert(result == button_up);
+ assert(action == &action_up);
+ }
+ }
+}
+
+/**
+ * Test refcounting of the common struct.
+ */
+static void
+test_common_ref(void)
+{
+ WacomCommonPtr common;
+ WacomCommonPtr second;
+
+ common = wcmNewCommon();
+ assert(common);
+ assert(common->refcnt == 1);
+
+ second = wcmRefCommon(common);
+
+ assert(second == common);
+ assert(second->refcnt == 2);
+
+ wcmFreeCommon(&second);
+ assert(common);
+ assert(!second);
+ assert(common->refcnt == 1);
+
+ second = wcmRefCommon(NULL);
+ assert(common != second);
+ assert(second->refcnt == 1);
+ assert(common->refcnt == 1);
+
+ wcmFreeCommon(&second);
+ wcmFreeCommon(&common);
+ assert(!second && !common);
+}
+
+
+static void
+test_rebase_pressure(void)
+{
+ WacomDeviceRec priv = {0};
+ WacomDeviceRec base = {0};
+ WacomDeviceState ds = {0};
+ int pressure;
+
+ priv.minPressure = 4;
+ ds.pressure = 10;
+
+ /* Pressure in out-of-proximity means get new preloaded pressure */
+ priv.oldProximity = 0;
+
+ /* make sure we don't touch priv, not really needed, the compiler should
+ * honor the consts but... */
+ base = priv;
+
+ pressure = rebasePressure(&priv, &ds);
+ assert(pressure == ds.pressure);
+
+ assert(memcmp(&priv, &base, sizeof(priv)) == 0);
+
+ /* Pressure in-proximity means rebase to new minimum */
+ priv.oldProximity = 1;
+
+ base = priv;
+
+ pressure = rebasePressure(&priv, &ds);
+ assert(pressure == priv.minPressure);
+ assert(memcmp(&priv, &base, sizeof(priv)) == 0);
+}
+
+static void
+test_normalize_pressure(void)
+{
+ InputInfoRec pInfo = {0};
+ WacomDeviceRec priv = {0};
+ WacomCommonRec common = {0};
+ WacomDeviceState ds = {0};
+ int pressure, prev_pressure = -1;
+ int i, j;
+
+ priv.common = &common;
+ priv.pInfo = &pInfo;
+ pInfo.name = "Wacom test device";
+
+ priv.minPressure = 0;
+
+ /* Some random loop to check various maxZ pressure values. Starting at
+ * 1, because if wcmMaxZ is 0 we have other problems. */
+ for (j = 1; j <= 256; j += 17)
+ {
+ common.wcmMaxZ = j;
+ prev_pressure = -1;
+
+ for (i = 0; i <= common.wcmMaxZ; i++)
+ {
+ ds.pressure = i;
+
+ pressure = normalizePressure(&priv, &ds);
+ assert(pressure >= 0);
+ assert(pressure <= FILTER_PRESSURE_RES);
+
+ /* we count up, so assume normalised pressure goes up too */
+ assert(prev_pressure < pressure);
+ prev_pressure = pressure;
+ }
+
+ assert(pressure == FILTER_PRESSURE_RES);
+ }
+
+ /* If minPressure is higher than ds->pressure, normalizePressure takes
+ * minPressure and ignores actual pressure. This would be a bug in the
+ * driver code, but we might as well test for it. */
+ priv.minPressure = 10;
+ ds.pressure = 0;
+
+ prev_pressure = normalizePressure(&priv, &ds);
+ for (i = 0; i < priv.minPressure; i++)
+ {
+ ds.pressure = i;
+
+ pressure = normalizePressure(&priv, &ds);
+
+ assert(pressure >= 0);
+ assert(pressure < FILTER_PRESSURE_RES);
+
+ /* we count up, so assume normalised pressure goes up too */
+ assert(prev_pressure == pressure);
+ }
+}
+
+/**
+ * After a call to wcmInitialToolSize, the min/max and resolution must be
+ * set up correctly.
+ *
+ * wcmInitialToolSize takes the data from the common rec, so test that the
+ * priv has all the values of the common.
+ */
+static void
+test_initial_size(void)
+{
+ InputInfoRec info = {0};
+ WacomDeviceRec priv = {0};
+ WacomCommonRec common = {0};
+
+ int minx, maxx, miny, maxy, xres, yres;
+
+ info.private = &priv;
+ priv.common = &common;
+
+ /* FIXME: we currently assume min of 0 in the driver. we cannot cope
+ * with non-zero devices */
+ minx = miny = 0;
+
+ common.wcmMaxX = maxx;
+ common.wcmMaxY = maxy;
+ common.wcmResolX = xres;
+ common.wcmResolY = yres;
+
+ wcmInitialToolSize(&info);
+
+ assert(priv.topX == minx);
+ assert(priv.topY == minx);
+ assert(priv.bottomX == maxx);
+ assert(priv.bottomY == maxy);
+ assert(priv.resolX == xres);
+ assert(priv.resolY == yres);
+
+ /* Same thing for a touch-enabled device */
+ memset(&common, 0, sizeof(common));
+
+ priv.flags = TOUCH_ID;
+ assert(IsTouch(&priv));
+
+ common.wcmMaxTouchX = maxx;
+ common.wcmMaxTouchY = maxy;
+ common.wcmTouchResolX = xres;
+ common.wcmTouchResolY = yres;
+
+ wcmInitialToolSize(&info);
+
+ assert(priv.topX == minx);
+ assert(priv.topY == minx);
+ assert(priv.bottomX == maxx);
+ assert(priv.bottomY == maxy);
+ assert(priv.resolX == xres);
+ assert(priv.resolY == yres);
+
+}
+
+static void
+test_suppress(void)
+{
+ enum WacomSuppressMode rc;
+ WacomCommonRec common = {0};
+ WacomDeviceState old = {0},
+ new = {0};
+
+ common.wcmSuppress = 2;
+
+ rc = wcmCheckSuppress(&common, &old, &new);
+ assert(rc == SUPPRESS_ALL);
+
+ /* proximity, buttons and strip send for any change */
+
+#define test_any_suppress(field) \
+ old.field = 1; \
+ rc = wcmCheckSuppress(&common, &old, &new); \
+ assert(rc == SUPPRESS_NONE); \
+ new.field = old.field;
+
+ test_any_suppress(proximity);
+ test_any_suppress(buttons);
+ test_any_suppress(stripx);
+ test_any_suppress(stripy);
+
+#undef test_any_suppress
+
+ /* pressure, capacity, throttle, rotation, abswheel only when
+ * difference is above suppress */
+
+ /* test negative and positive transition */
+#define test_above_suppress(field) \
+ old.field = common.wcmSuppress; \
+ rc = wcmCheckSuppress(&common, &old, &new); \
+ assert(rc == SUPPRESS_ALL); \
+ old.field = common.wcmSuppress + 1; \
+ rc = wcmCheckSuppress(&common, &old, &new); \
+ assert(rc == SUPPRESS_NONE); \
+ old.field = -common.wcmSuppress; \
+ rc = wcmCheckSuppress(&common, &old, &new); \
+ assert(rc == SUPPRESS_ALL); \
+ old.field = -common.wcmSuppress - 1; \
+ rc = wcmCheckSuppress(&common, &old, &new); \
+ assert(rc == SUPPRESS_NONE); \
+ new.field = old.field;
+
+ test_above_suppress(pressure);
+ test_above_suppress(throttle);
+ test_above_suppress(rotation);
+ test_above_suppress(abswheel);
+
+#undef test_above_suppress
+
+ /* any movement on relwheel counts */
+ new.relwheel = 1;
+ rc = wcmCheckSuppress(&common, &old, &new);
+ assert(rc == SUPPRESS_NONE);
+ new.relwheel = 0;
+
+ /* x axis movement */
+
+ /* not enough movement */
+ new.x = common.wcmSuppress;
+ rc = wcmCheckSuppress(&common, &old, &new);
+ assert(rc == SUPPRESS_ALL);
+ assert(old.x == new.x);
+ assert(old.y == new.y);
+
+ /* only x axis above thresh */
+ new.x = common.wcmSuppress + 1;
+ rc = wcmCheckSuppress(&common, &old, &new);
+ assert(rc == SUPPRESS_NON_MOTION);
+
+ /* x and other field above thres */
+ new.pressure = ~old.pressure;
+ rc = wcmCheckSuppress(&common, &old, &new);
+ assert(rc == SUPPRESS_NONE);
+
+ new.pressure = old.pressure;
+ new.x = old.x;
+
+ /* y axis movement */
+ new.y = common.wcmSuppress;
+ rc = wcmCheckSuppress(&common, &old, &new);
+ assert(rc == SUPPRESS_ALL);
+ assert(old.x == new.x);
+ assert(old.y == new.y);
+
+ new.y = common.wcmSuppress + 1;
+ rc = wcmCheckSuppress(&common, &old, &new);
+ assert(rc == SUPPRESS_NON_MOTION);
+
+ new.pressure = ~old.pressure;
+ rc = wcmCheckSuppress(&common, &old, &new);
+ assert(rc == SUPPRESS_NONE);
+ new.pressure = old.pressure;
+}
+
+static void
+test_tilt_to_rotation(void)
+{
+#if 0
+ This table below was generated from wcmTilt2R with the following code
+
+ for (angle = 0; angle < 360; angle++)
+ {
+ double rad = angle * M_PI / 180.0;
+ double x, y;
+ x = sin(rad);
+ y = cos(rad);
+
+ /* wcmTilt2R only uses it for the angle anyway, let's try to
+ get as precise as possible */
+ ds.tiltx = x * 1000;
+ ds.tilty = y * 1000;
+ ds.rotation = 0;
+
+ wcmTilt2R(&ds);
+
+ printf("{ %d, %d, %d},\n", ds.tiltx, ds.tilty, ds.rotation);
+ }
+#endif
+
+ int rotation_table[][3] = {
+ { 17, 999, 20}, { 34, 999, 15}, { 52, 998, 10}, { 69, 997, 5}, { 87, 996, 0},
+ { 104, 994, -5}, { 121, 992, -10}, { 139, 990, -15}, { 156, 987, -20}, { 173, 984, -25},
+ { 190, 981, -30}, { 207, 978, -35}, { 224, 974, -40}, { 241, 970, -45}, { 258, 965, -50},
+ { 275, 961, -55}, { 292, 956, -60}, { 309, 951, -65}, { 325, 945, -70}, { 342, 939, -75},
+ { 358, 933, -80}, { 374, 927, -85}, { 390, 920, -90}, { 406, 913, -95}, { 422, 906, -100},
+ { 438, 898, -105}, { 453, 891, -110}, { 469, 882, -115}, { 484, 874, -120}, { 499, 866, -125},
+ { 515, 857, -130}, { 529, 848, -135}, { 544, 838, -140}, { 559, 829, -145}, { 573, 819, -150},
+ { 587, 809, -155}, { 601, 798, -160}, { 615, 788, -165}, { 629, 777, -170}, { 642, 766, -175},
+ { 656, 754, -180}, { 669, 743, -185}, { 681, 731, -190}, { 694, 719, -195}, { 707, 707, -200},
+ { 719, 694, -205}, { 731, 681, -210}, { 743, 669, -215}, { 754, 656, -220}, { 766, 642, -225},
+ { 777, 629, -230}, { 788, 615, -235}, { 798, 601, -240}, { 809, 587, -245}, { 819, 573, -250},
+ { 829, 559, -255}, { 838, 544, -260}, { 848, 529, -265}, { 857, 515, -270}, { 866, 500, -275},
+ { 874, 484, -280}, { 882, 469, -285}, { 891, 453, -290}, { 898, 438, -295}, { 906, 422, -300},
+ { 913, 406, -305}, { 920, 390, -310}, { 927, 374, -315}, { 933, 358, -320}, { 939, 342, -325},
+ { 945, 325, -330}, { 951, 309, -335}, { 956, 292, -340}, { 961, 275, -345}, { 965, 258, -350},
+ { 970, 241, -355}, { 974, 224, -360}, { 978, 207, -365}, { 981, 190, -370}, { 984, 173, -375},
+ { 987, 156, -380}, { 990, 139, -385}, { 992, 121, -390}, { 994, 104, -395}, { 996, 87, -400},
+ { 997, 69, -405}, { 998, 52, -410}, { 999, 34, -415}, { 999, 17, -420}, { 1000, 0, -425},
+ { 999, -17, -430}, { 999, -34, -435}, { 998, -52, -440}, { 997, -69, -445}, { 996, -87, -450},
+ { 994, -104, -455}, { 992, -121, -460}, { 990, -139, -465}, { 987, -156, -470}, { 984, -173, -475},
+ { 981, -190, -480}, { 978, -207, -485}, { 974, -224, -490}, { 970, -241, -495}, { 965, -258, -500},
+ { 961, -275, -505}, { 956, -292, -510}, { 951, -309, -515}, { 945, -325, -520}, { 939, -342, -525},
+ { 933, -358, -530}, { 927, -374, -535}, { 920, -390, -540}, { 913, -406, -545}, { 906, -422, -550},
+ { 898, -438, -555}, { 891, -453, -560}, { 882, -469, -565}, { 874, -484, -570}, { 866, -499, -575},
+ { 857, -515, -580}, { 848, -529, -585}, { 838, -544, -590}, { 829, -559, -595}, { 819, -573, -600},
+ { 809, -587, -605}, { 798, -601, -610}, { 788, -615, -615}, { 777, -629, -620}, { 766, -642, -625},
+ { 754, -656, -630}, { 743, -669, -635}, { 731, -681, -640}, { 719, -694, -645}, { 707, -707, -650},
+ { 694, -719, -655}, { 681, -731, -660}, { 669, -743, -665}, { 656, -754, -670}, { 642, -766, -675},
+ { 629, -777, -680}, { 615, -788, -685}, { 601, -798, -690}, { 587, -809, -695}, { 573, -819, -700},
+ { 559, -829, -705}, { 544, -838, -710}, { 529, -848, -715}, { 515, -857, -720}, { 499, -866, -725},
+ { 484, -874, -730}, { 469, -882, -735}, { 453, -891, -740}, { 438, -898, -745}, { 422, -906, -750},
+ { 406, -913, -755}, { 390, -920, -760}, { 374, -927, -765}, { 358, -933, -770}, { 342, -939, -775},
+ { 325, -945, -780}, { 309, -951, -785}, { 292, -956, -790}, { 275, -961, -795}, { 258, -965, -800},
+ { 241, -970, -805}, { 224, -974, -810}, { 207, -978, -815}, { 190, -981, -820}, { 173, -984, -825},
+ { 156, -987, -830}, { 139, -990, -835}, { 121, -992, -840}, { 104, -994, -845}, { 87, -996, -850},
+ { 69, -997, -855}, { 52, -998, -860}, { 34, -999, -865}, { 17, -999, -870}, { 0, -1000, -875},
+ { -17, -999, -880}, { -34, -999, -885}, { -52, -998, -890}, { -69, -997, -895}, { -87, -996, -900},
+ { -104, -994, 895}, { -121, -992, 890}, { -139, -990, 885}, { -156, -987, 880}, { -173, -984, 875},
+ { -190, -981, 870}, { -207, -978, 865}, { -224, -974, 860}, { -241, -970, 855}, { -258, -965, 850},
+ { -275, -961, 845}, { -292, -956, 840}, { -309, -951, 835}, { -325, -945, 830}, { -342, -939, 825},
+ { -358, -933, 820}, { -374, -927, 815}, { -390, -920, 810}, { -406, -913, 805}, { -422, -906, 800},
+ { -438, -898, 795}, { -453, -891, 790}, { -469, -882, 785}, { -484, -874, 780}, { -500, -866, 775},
+ { -515, -857, 770}, { -529, -848, 765}, { -544, -838, 760}, { -559, -829, 755}, { -573, -819, 750},
+ { -587, -809, 745}, { -601, -798, 740}, { -615, -788, 735}, { -629, -777, 730}, { -642, -766, 725},
+ { -656, -754, 720}, { -669, -743, 715}, { -681, -731, 710}, { -694, -719, 705}, { -707, -707, 700},
+ { -719, -694, 695}, { -731, -681, 690}, { -743, -669, 685}, { -754, -656, 680}, { -766, -642, 675},
+ { -777, -629, 670}, { -788, -615, 665}, { -798, -601, 660}, { -809, -587, 655}, { -819, -573, 650},
+ { -829, -559, 645}, { -838, -544, 640}, { -848, -529, 635}, { -857, -515, 630}, { -866, -500, 625},
+ { -874, -484, 620}, { -882, -469, 615}, { -891, -453, 610}, { -898, -438, 605}, { -906, -422, 600},
+ { -913, -406, 595}, { -920, -390, 590}, { -927, -374, 585}, { -933, -358, 580}, { -939, -342, 575},
+ { -945, -325, 570}, { -951, -309, 565}, { -956, -292, 560}, { -961, -275, 555}, { -965, -258, 550},
+ { -970, -241, 545}, { -974, -224, 540}, { -978, -207, 535}, { -981, -190, 530}, { -984, -173, 525},
+ { -987, -156, 520}, { -990, -139, 515}, { -992, -121, 510}, { -994, -104, 505}, { -996, -87, 500},
+ { -997, -69, 495}, { -998, -52, 490}, { -999, -34, 485}, { -999, -17, 480}, { -1000, 0, 475},
+ { -999, 17, 470}, { -999, 34, 465}, { -998, 52, 460}, { -997, 69, 455}, { -996, 87, 450},
+ { -994, 104, 445}, { -992, 121, 440}, { -990, 139, 435}, { -987, 156, 430}, { -984, 173, 425},
+ { -981, 190, 420}, { -978, 207, 415}, { -974, 224, 410}, { -970, 241, 405}, { -965, 258, 400},
+ { -961, 275, 395}, { -956, 292, 390}, { -951, 309, 385}, { -945, 325, 380}, { -939, 342, 375},
+ { -933, 358, 370}, { -927, 374, 365}, { -920, 390, 360}, { -913, 406, 355}, { -906, 422, 350},
+ { -898, 438, 345}, { -891, 453, 340}, { -882, 469, 335}, { -874, 484, 330}, { -866, 500, 325},
+ { -857, 515, 320}, { -848, 529, 315}, { -838, 544, 310}, { -829, 559, 305}, { -819, 573, 300},
+ { -809, 587, 295}, { -798, 601, 290}, { -788, 615, 285}, { -777, 629, 280}, { -766, 642, 275},
+ { -754, 656, 270}, { -743, 669, 265}, { -731, 681, 260}, { -719, 694, 255}, { -707, 707, 250},
+ { -694, 719, 245}, { -681, 731, 240}, { -669, 743, 235}, { -656, 754, 230}, { -642, 766, 225},
+ { -629, 777, 220}, { -615, 788, 215}, { -601, 798, 210}, { -587, 809, 205}, { -573, 819, 200},
+ { -559, 829, 195}, { -544, 838, 190}, { -529, 848, 185}, { -515, 857, 180}, { -500, 866, 175},
+ { -484, 874, 170}, { -469, 882, 165}, { -453, 891, 160}, { -438, 898, 155}, { -422, 906, 150},
+ { -406, 913, 145}, { -390, 920, 140}, { -374, 927, 135}, { -358, 933, 130}, { -342, 939, 125},
+ { -325, 945, 120}, { -309, 951, 115}, { -292, 956, 110}, { -275, 961, 105}, { -258, 965, 100},
+ { -241, 970, 95}, { -224, 974, 90}, { -207, 978, 85}, { -190, 981, 80}, { -173, 984, 75},
+ { -156, 987, 70}, { -139, 990, 65}, { -121, 992, 60}, { -104, 994, 55}, { -87, 996, 50},
+ { -69, 997, 45}, { -52, 998, 40}, { -34, 999, 35}, { -17, 999, 30},
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rotation_table); i++)
+ {
+ int rotation;
+ int x, y;
+ x = rotation_table[i][0];
+ y = rotation_table[i][1];
+ rotation = wcmTilt2R(x, y, INTUOS4_CURSOR_ROTATION_OFFSET);
+ assert(rotation == rotation_table[i][2]);
+ }
+}
+
+
+static void
+test_mod_buttons(void)
+{
+ int i;
+ for (i = 0; i < sizeof(int) * 8; i++)
+ {
+ int buttons = mod_buttons(0, i, 1);
+ assert(buttons == (1 << i));
+ buttons = mod_buttons(0, i, 0);
+ assert(buttons == 0);
+ }
+
+ assert(mod_buttons(0, sizeof(int) * 8, 1) == 0);
+}
+
+static void test_set_type(void)
+{
+ InputInfoRec info = {0};
+ WacomDeviceRec priv = {0};
+ WacomTool tool = {0};
+ WacomCommonRec common = {0};
+ int rc;
+
+#define reset(_info, _priv, _tool, _common) \
+ memset(&(_info), 0, sizeof(_info)); \
+ memset(&(_priv), 0, sizeof(_priv)); \
+ memset(&(_tool), 0, sizeof(_tool)); \
+ (_info).private = &(_priv); \
+ (_priv).tool = &(_tool); \
+ (_priv).common = &(_common);
+
+
+ reset(info, priv, tool, common);
+ rc = wcmSetType(&info, NULL);
+ assert(rc == 0);
+
+ reset(info, priv, tool, common);
+ rc = wcmSetType(&info, "stylus");
+ assert(rc == 1);
+ assert(is_absolute(&info));
+ assert(IsStylus(&priv));
+ assert(!IsTouch(&priv));
+ assert(!IsEraser(&priv));
+ assert(!IsCursor(&priv));
+ assert(!IsPad(&priv));
+
+ reset(info, priv, tool, common);
+ rc = wcmSetType(&info, "touch");
+ assert(rc == 1);
+ /* only some touch screens are absolute */
+ assert(!is_absolute(&info));
+ assert(!IsStylus(&priv));
+ assert(IsTouch(&priv));
+ assert(!IsEraser(&priv));
+ assert(!IsCursor(&priv));
+ assert(!IsPad(&priv));
+
+ reset(info, priv, tool, common);
+ rc = wcmSetType(&info, "eraser");
+ assert(rc == 1);
+ assert(is_absolute(&info));
+ assert(!IsStylus(&priv));
+ assert(!IsTouch(&priv));
+ assert(IsEraser(&priv));
+ assert(!IsCursor(&priv));
+ assert(!IsPad(&priv));
+
+ reset(info, priv, tool, common);
+ rc = wcmSetType(&info, "cursor");
+ assert(rc == 1);
+ assert(!is_absolute(&info));
+ assert(!IsStylus(&priv));
+ assert(!IsTouch(&priv));
+ assert(!IsEraser(&priv));
+ assert(IsCursor(&priv));
+ assert(!IsPad(&priv));
+
+ reset(info, priv, tool, common);
+ rc = wcmSetType(&info, "pad");
+ assert(rc == 1);
+ assert(is_absolute(&info));
+ assert(!IsStylus(&priv));
+ assert(!IsTouch(&priv));
+ assert(!IsEraser(&priv));
+ assert(!IsCursor(&priv));
+ assert(IsPad(&priv));
+
+ reset(info, priv, tool, common);
+ rc = wcmSetType(&info, "foobar");
+ assert(rc == 0);
+
+#undef reset
+}
+
+static int test_flag_set(void)
+{
+ int i;
+ unsigned int flags = 0;
+
+ for (i = 0; i < sizeof(flags); i++)
+ {
+ int mask = 1 << i;
+ flags = 0;
+
+ assert(!MaskIsSet(flags, mask));
+ MaskSet(flags, mask);
+ assert(flags != 0);
+ assert(MaskIsSet(flags, mask));
+ MaskClear(flags, mask);
+ assert(!MaskIsSet(flags, mask));
+ assert(flags == 0);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ test_common_ref();
+ test_rebase_pressure();
+ test_normalize_pressure();
+ test_suppress();
+ test_initial_size();
+ test_tilt_to_rotation();
+ test_mod_buttons();
+ test_set_type();
+ test_flag_set();
+ test_get_scroll_delta();
+ test_get_wheel_button();
+ return 0;
+}
+
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/tools/.gitignore b/tools/.gitignore
index 678631e..f80cd88 100644
--- a/tools/.gitignore
+++ b/tools/.gitignore
@@ -1,2 +1,3 @@
# Add & Override for this directory and it's subdirectories
xsetwacom
+isdv4-serial-debugger
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 5bce44b..5cd40e2 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -17,12 +17,18 @@
# 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.
-AM_CFLAGS = $(XORG_CFLAGS) $(X11_CFLAGS)
-bin_PROGRAMS = \
- xsetwacom
+bin_PROGRAMS = xsetwacom isdv4-serial-debugger
+
+AM_CPPFLAGS = -I$(top_srcdir)/include
+AM_CFLAGS = $(XORG_CFLAGS) $(X11_CFLAGS)
+AM_LDFLAGS = $(X11_LIBS)
-INCLUDES=-I$(top_srcdir)/include/
-xsetwacom_LDFLAGS = $(X11_LIBS) -lm
+if UNITTESTS
+check_PROGRAMS = xsetwacom-test
+xsetwacom_test_SOURCES=xsetwacom.c
+xsetwacom_test_CFLAGS=-DBUILD_TEST $(X11_CFLAGS)
+TESTS=$(check_PROGRAMS)
+endif
diff --git a/tools/isdv4-serial-debugger.c b/tools/isdv4-serial-debugger.c
new file mode 100644
index 0000000..8a64f43
--- /dev/null
+++ b/tools/isdv4-serial-debugger.c
@@ -0,0 +1,589 @@
+/*
+ * Copyright 2010 by Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Simple protocol debugger for ISDV4 serial devices */
+
+#ifdef HAVE_CONFIG_H
+#define WACOM_TOOLS
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/serial.h>
+#include <getopt.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "isdv4.h"
+
+#define TRACE(...) \
+ do { if (verbose) printf("... " __VA_ARGS__); } while(0)
+
+static int verbose = 0;
+static int packetlength = ISDV4_PKGLEN_TPCPEN;
+static ISDV4QueryReply reply;
+static ISDV4TouchQueryReply touch;
+
+
+static void usage(void)
+{
+ printf(
+ "Usage: wacom-serial-debugger [options] device\n"
+ "Options:\n"
+ " -h, --help - usage\n"
+ " -v, --verbose - verbose output\n"
+ " -V, --version - version info\n"
+ " -b, --baudrate baudrate - set baudrate\n"
+ " --reset - send reset command before doing anything\n");
+}
+
+static void version(void)
+{
+ printf("%d.%d.%d\n", PACKAGE_VERSION_MAJOR, PACKAGE_VERSION_MINOR,
+ PACKAGE_VERSION_PATCHLEVEL);
+}
+
+static int open_device(char *path)
+{
+ int fd, rc;
+ struct serial_struct ser;
+ struct termios t;
+
+ TRACE("Opening device '%s'.\n", path);
+ fd = open(path, O_RDWR);
+
+ if (fd < 1)
+ perror("Failed to open device file.");
+
+ if (ioctl(fd, TIOCGSERIAL, &ser) == -1)
+ {
+ fprintf(stderr, "Not a serial device?");
+ close(fd);
+ fd = -1;
+ goto out;
+ }
+
+ rc = tcgetattr(fd, &t);
+ if (rc == -1)
+ {
+ perror("Failed to get serial attributes.");
+ close(fd);
+ fd = -1;
+ goto out;
+ }
+
+ /* defaults from xf86OpenSerial */
+ t.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+ t.c_oflag &= ~OPOST;
+ t.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+ t.c_cflag &= ~(CSIZE|PARENB);
+ t.c_cflag |= CS8|CLOCAL;
+ tcsetattr(fd, TCSANOW, &t);
+
+ /* wacom-specific */
+ t.c_cflag &= ~(CSTOPB); /* stopbits 1 */
+ t.c_cflag &= ~(CSIZE); /* databits 8 */
+ t.c_cflag |= (CS8); /* databits 8 */
+ t.c_cflag &= ~(PARENB); /* parity none */
+ t.c_cc[VMIN] = 1; /* vmin 1 */
+ t.c_cc[VTIME] = 10; /* vtime 10 */
+ t.c_iflag |= IXOFF; /* flow controll xoff */
+
+
+ tcsetattr(fd, TCSANOW, &t);
+
+out:
+ return fd;
+}
+
+static int set_baud_rate(int fd, int baud)
+{
+ struct termios t;
+ int rc;
+
+ TRACE("Baud rate is %d\n", baud);
+
+ switch(baud)
+ {
+ case 19200: baud = B19200; break;
+ case 38400: baud = B38400; break;
+ default:
+ fprintf(stderr, "Unsupported baud rate.\n");
+ return -1;
+ }
+
+ rc = tcgetattr(fd, &t);
+
+ if (rc)
+ return rc;
+
+ cfsetispeed(&t, baud);
+ cfsetospeed(&t, baud);
+
+ return tcsetattr(fd, TCSANOW, &t);
+}
+
+static int write_to_tablet(int fd, char *command)
+{
+ int len = 0;
+
+ do {
+ int l;
+ l = write(fd, &command[len], strlen(command) - len);
+
+ TRACE("Written '%s'.\n", command);
+
+ if (l == -1 && errno != EAGAIN)
+ {
+ perror("not written.\n");
+ break;
+ }
+ len += l;
+ } while (errno == EAGAIN && len < strlen(command));
+
+ return !(len == strlen(command));
+}
+
+static int stop_tablet(int fd)
+{
+ int rc;
+ char buffer[10];
+ int fd_flags;
+
+ TRACE("Writing STOP command.\n");
+ rc = write_to_tablet(fd, ISDV4_STOP);
+
+ usleep(250000);
+
+ /* flush the line */
+ fd_flags = fcntl(fd, F_GETFL);
+ if (fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK) == 0)
+ {
+ while (read(fd, buffer, sizeof(buffer)) > 0)
+ TRACE("garbage flushed\n");
+ fcntl(fd, F_SETFL, fd_flags);
+ }
+
+ return rc;
+}
+
+static int start_tablet(int fd)
+{
+ TRACE("Writing SAMPLING command.\n");
+ return write_to_tablet(fd, ISDV4_SAMPLING);
+}
+
+static int wait_for_tablet(int fd)
+{
+#if 0
+ struct pollfd pfd = { fd, POLLIN, 0 };
+ int rc;
+
+ TRACE("Waiting for tablet...");
+ rc = poll(&pfd, 1, 1000);
+ if (rc < 0) {
+ perror("poll failed.");
+ return -1;
+ } else if (rc == 0) {
+ fprintf(stderr, "timeout.\n");
+ return -1;
+ } else if (pfd.revents & POLLIN)
+ TRACE("data available.\n");
+
+#endif
+ return 0;
+}
+
+static void memdump(const unsigned char *buffer, int len)
+{
+ int n = 0;
+ if (!len)
+ return;
+
+ while(len-- && ++n) {
+ TRACE("%#hhx ", *buffer++);
+ if (n % 8 == 0)
+ TRACE("\n");
+ }
+
+ TRACE("\n");
+}
+
+static int skip_garbage(unsigned char *buffer, size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ if (buffer[i] & HEADER_BIT)
+ break;
+
+ if (i != 0)
+ TRACE("skipping over %d bytes.\n", (i < len) ? i : -1);
+
+ return (i < len) ? i : -1;
+}
+
+static int read_data(int fd, unsigned char* buffer, int min_len)
+{
+ int len = 0;
+ int attempts = 10;
+ int skip;
+
+ TRACE("Reading %d bytes from device.\n", min_len);
+redo:
+ do {
+ int l = read(fd, &buffer[len], min_len);
+
+ if (l == -1) {
+ if (errno != EAGAIN) {
+ perror("Error reading data.");
+ return -1;
+ }
+ wait_for_tablet(fd);
+ attempts--;
+ continue;
+ } else {
+ TRACE("read %d bytes in one chunk.\n", l);
+ len += l;
+ }
+
+ } while (len < min_len && attempts);
+
+ if (!attempts) {
+ fprintf(stderr, "Only able to read %d bytes.\n", len);
+ memdump(buffer, len);
+ return -1;
+ }
+
+ TRACE("Read %d bytes.\n", len);
+
+ skip = skip_garbage(buffer, len);
+ if (skip > 0) {
+ TRACE("%d bytes garbage.\n", skip);
+ len -= skip;
+ memmove(buffer, &buffer[skip], len);
+ goto redo;
+ }
+
+ if (len > min_len)
+ {
+ TRACE("%d bytes unexpected data.\n", (len - min_len));
+ memdump(&buffer[min_len], len - min_len);
+ }
+
+
+ return len;
+}
+
+static int query_tablet(int fd)
+{
+ unsigned char buffer[ISDV4_PKGLEN_TPCCTL];
+ int len, rc;
+
+ TRACE("Querying tablet.\n");
+
+ if (stop_tablet(fd)) goto out;
+ if (write_to_tablet(fd, ISDV4_QUERY)) goto out;
+ if (wait_for_tablet(fd)) goto out;
+
+ len = read_data(fd, buffer, ISDV4_PKGLEN_TPCCTL);
+ if (len < 1)
+ goto out;
+
+ if (!(buffer[0] & CONTROL_BIT))
+ {
+ TRACE("+++ out of cheese error +++ redo from start +++\n");
+ /* X driver claims that the first read may fail ??? */
+ len = read_data(fd, buffer, ISDV4_PKGLEN_TPCCTL);
+ }
+
+ TRACE("Parsing query reply.\n");
+ rc = isdv4ParseQuery(buffer, len, &reply);
+ if (rc < 0)
+ {
+ fprintf(stderr, "parsing error code %d\n", rc);
+ goto out;
+ }
+
+ printf("TABLET: version: %d\n", reply.version);
+ printf("TABLET: x max: %d y max %d\n", reply.x_max, reply.y_max);
+ printf("TABLET: tilt_x max: %d tilt_y max %d\n", reply.tilt_x_max, reply.tilt_y_max);
+ printf("TABLET: pressure max: %d\n", reply.pressure_max);
+
+ /* check for touch capabilities */
+ TRACE("Trying touch query\n");
+ if (stop_tablet(fd)) goto out;
+ if (write_to_tablet(fd, ISDV4_TOUCH_QUERY)) goto out;
+ if (wait_for_tablet(fd)) goto out;
+
+ memset(buffer, 0, sizeof(buffer));
+ len = read_data(fd, buffer, ISDV4_PKGLEN_TPCCTL);
+ if (len < 1 && errno != EAGAIN)
+ goto out;
+
+ TRACE("Parsing touch query reply.\n");
+ rc = isdv4ParseTouchQuery(buffer, len, &touch);
+ if (rc < 0)
+ {
+ fprintf(stderr, "touch parsing error code %d\n", rc);
+ /* failure to parse touch query is not fatal */
+ } else {
+ printf("TOUCH version: %d\n", touch.version);
+ printf("TOUCH x max: %d y max %d\n", touch.x_max, touch.y_max);
+ printf("TOUCH panel resolution: %d\n", touch.panel_resolution);
+ printf("TOUCH capacity resolution: %d\n", touch.capacity_resolution);
+ printf("TOUCH sensor id: %d\n", touch.sensor_id);
+ }
+
+ return 0;
+
+out:
+ fprintf(stderr, "error during query.\n");
+ return -1;
+}
+
+static int reset_tablet(int fd)
+{
+ char buffer[10];
+ TRACE("Reset requested, resetting tablet\n");
+
+ if (stop_tablet(fd)) goto out;
+ if (write_to_tablet(fd, ISDV4_RESET)) goto out;
+ if (wait_for_tablet(fd)) goto out;
+
+ memset(buffer, 0, sizeof(buffer));
+ if (read(fd, buffer, sizeof(buffer)) < 1 && errno != EAGAIN)
+ goto out;
+
+ if (buffer[0] == '&')
+ return 0;
+
+out:
+ fprintf(stderr, "failed to reset tablet.\n");
+ return 1;
+}
+
+static int parse_pen_packet(unsigned char* buffer)
+{
+ int rc;
+ ISDV4CoordinateData coord;
+
+ TRACE("Parsing coordinate data.\n");
+ rc = isdv4ParseCoordinateData(buffer, ISDV4_PKGLEN_TPCPEN, &coord);
+ if (rc == -1) {
+ fprintf(stderr, "failed to parse coordinate data.\n");
+ return -1;
+ }
+
+ printf("PEN ");
+ printf("%ld:", time(NULL));
+ printf("%5d/%5d | pressure: %3d | ", coord.x, coord.y, coord.pressure);
+ printf(" %3d/%3d |", coord.tilt_x, coord.tilt_y);
+ printf("%1s %1s %1s %1s |\n", coord.proximity ? "p" : "",
+ coord.tip ? "t" : "",
+ coord.side ? "s" : "",
+ coord.eraser ? "e" : "");
+ return 0;
+}
+
+static int parse_touch_packet(unsigned char* buffer, int packetlength)
+{
+ ISDV4TouchData touchdata;
+ int rc;
+
+ rc = isdv4ParseTouchData(buffer, packetlength, packetlength, &touchdata);
+ if (rc == -1) {
+ fprintf(stderr, "failed to parse touch data.\n");
+ return -1;
+ }
+
+ printf("TOUCH ");
+ printf("%ld:", time(NULL));
+ printf("%5d/%5d | capacity: %3d | ", touchdata.x, touchdata.y, touchdata.capacity);
+ printf("%d | ", touchdata.status);
+
+ if (packetlength == ISDV4_PKGLEN_TOUCH2FG)
+ printf("%d | %d/%d |", touchdata.finger2.status, touchdata.finger2.x, touchdata.finger2.y);
+
+ printf("\n");
+
+ return 0;
+
+}
+
+int event_loop(int fd)
+{
+ unsigned char buffer[256];
+ int dlen = 0;
+
+ TRACE("Waiting for events\n");
+
+ if (fcntl(fd, F_SETFD, O_NONBLOCK) == -1)
+ perror("Nonblock failed.");
+
+ memset(buffer, 0, sizeof(buffer));
+
+ while (1) {
+ int r, garbage = 0;
+ r = read(fd, &buffer[dlen], sizeof(buffer) - dlen);
+
+ if (r == -1) {
+ if (errno == EAGAIN)
+ continue;
+ else {
+ perror("Error during read.");
+ goto out;
+ }
+ }
+
+ dlen += r;
+
+ if (buffer[0] & HEADER_BIT)
+ {
+ packetlength = ISDV4_PKGLEN_TPCPEN;
+ if (buffer[0] & TOUCH_CONTROL_BIT)
+ packetlength = ISDV4PacketLengths[touch.sensor_id];
+ } else {
+ int bytes = skip_garbage(buffer, dlen);
+ if (bytes > 0) {
+ dlen -= bytes;
+ memmove(buffer, &buffer[bytes], sizeof(buffer) - bytes);
+ }
+ continue;
+ }
+
+
+ if (dlen < packetlength)
+ continue;
+ TRACE("Expecting packet sized %d\n", packetlength);
+
+ if (buffer[0] & CONTROL_BIT) {
+ dlen -= packetlength;
+ continue;
+ }
+
+ switch(packetlength)
+ {
+ case ISDV4_PKGLEN_TPCPEN:
+ if (parse_pen_packet(buffer))
+ garbage = 1;
+ break;
+ default: /* all others */
+ if (parse_touch_packet(buffer, packetlength))
+ garbage = 1;
+ }
+
+ if (garbage) {
+ int bytes;
+ bytes = skip_garbage(buffer, packetlength);
+ if (bytes > 0) {
+ dlen -= bytes;
+ memmove(buffer, &buffer[bytes], sizeof(buffer) - bytes);
+ }
+ garbage = 0;
+ } else {
+ memmove(buffer, &buffer[packetlength], sizeof(buffer) - packetlength);
+ dlen -= packetlength;
+ }
+ }
+
+
+ return 0;
+out:
+ return 1;
+}
+
+int main (int argc, char **argv)
+{
+ int fd;
+ char *filename;
+ int baudrate = 38400;
+ int reset = 0;
+ int rc;
+
+ int c, optidx = 0;
+ struct option options[] = {
+ {"help", 0, NULL, 'h'},
+ {"verbose", 0, NULL, 'v'},
+ {"version", 0, NULL, 'V'},
+ {"baudrate", 1, NULL, 'b'},
+ {"reset", 0, NULL, 'r' },
+ {NULL, 0, NULL, 0}
+ };
+
+ while ((c = getopt_long(argc, argv, "+hvVb:", options, &optidx)) != -1) {
+ switch(c) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 'V':
+ version();
+ return 0;
+ case 'b':
+ baudrate = atoi(optarg);
+ break;
+ case 'r':
+ reset = 1;
+ break;
+ case 'h':
+ default:
+ usage();
+ return 0;
+ }
+ }
+
+ if (optind == argc) {
+ usage();
+ return 0;
+ }
+
+ filename = argv[optind];
+
+ fd = open_device(filename);
+ if (fd < 0)
+ return 1;
+
+ rc = set_baud_rate(fd, baudrate);
+ if (rc < 0)
+ return 1;
+
+ if (reset) {
+ rc = reset_tablet(fd);
+ if (rc < 0)
+ return 1;
+ }
+
+ rc = query_tablet(fd);
+ if (rc < 0)
+ return 1;
+
+ start_tablet(fd);
+
+ return event_loop(fd);
+}
+
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */
diff --git a/tools/xsetwacom.c b/tools/xsetwacom.c
index e4dd501..9ab285b 100644
--- a/tools/xsetwacom.c
+++ b/tools/xsetwacom.c
@@ -22,8 +22,11 @@
#endif
#include <wacom-properties.h>
+#include <wacom-util.h>
#include "Xwacom.h"
+#include <errno.h>
+#include <limits.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
@@ -34,6 +37,8 @@
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/XInput.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/extensions/Xinerama.h>
#include <X11/XKBlib.h>
#define TRACE(...) \
@@ -50,9 +55,32 @@ enum printformat {
enum prop_flags {
PROP_FLAG_BOOLEAN = 1,
PROP_FLAG_READONLY = 2,
- PROP_FLAG_WRITEONLY = 4
+ PROP_FLAG_WRITEONLY = 4,
+ PROP_FLAG_INVERTED = 8, /* only valid with PROP_FLAG_BOOLEAN */
+ PROP_FLAG_OUTPUT = 16,
};
+
+/**
+ * How this works:
+ * Each parameter supported by xsetwacom has a struct param_t in the global
+ * parameters[] array.
+ * For 'standard' parameters that just modify a property, the prop_* fields
+ * are set to the matching property, format and offset. The get() function
+ * then handles the retrieval of the property, the set() function handles
+ * the modification of the property.
+ *
+ * For parameters that need more than just triggering a property, the
+ * set_func and get_func point to the matching function to modify that
+ * particular parameter. example are the ButtonX parameters that call
+ * XSetDeviceButtonMapping instead of triggering a property.
+ *
+ * device_name is filled in automatically and just used to pass around the
+ * device name (since the XDevice* doesn't store this info). printformat is
+ * a flag that changes the output required, so that the -c and -s
+ * commandline arguments work.
+ */
+
typedef struct _param
{
const char *name; /* param name as specified by the user */
@@ -60,7 +88,7 @@ typedef struct _param
const char *prop_name; /* property name */
const int prop_format; /* property format */
const int prop_offset; /* offset (index) into the property values */
- const int prop_extra; /* extra number of items after first one */
+ const int arg_count; /* extra number of items after first one */
const unsigned int prop_flags;
void (*set_func)(Display *dpy, XDevice *dev, struct _param *param, int argc, char **argv); /* handler function, if appropriate */
void (*get_func)(Display *dpy, XDevice *dev, struct _param *param, int argc, char **argv); /* handler function for getting, if appropriate */
@@ -70,759 +98,472 @@ typedef struct _param
enum printformat printformat;
} param_t;
-static void map_button(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
+
+/* get_func/set_func calls for special parameters */
+static void map_actions(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
static void set_mode(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
static void get_mode(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
-static void get_presscurve(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
-static void get_button(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
+static void get_map(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
static void set_rotate(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
static void get_rotate(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
-static void set_twinview(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
-static void get_twinview(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
static void set_xydefault(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
static void get_all(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
static void get_param(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
-static void not_implemented(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv)
-{
- printf("Not implemented.\n");
-}
+static void set_output(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv);
+/* NOTE: When removing or changing a parameter name, add to
+ * deprecated_parameters.
+ */
static param_t parameters[] =
{
{
- .name = "TopX",
- .desc = "Bounding rect left coordinate in tablet units. ",
+ .name = "Area",
+ .desc = "Valid tablet area in device coordinates. ",
.prop_name = WACOM_PROP_TABLET_AREA,
.prop_format = 32,
.prop_offset = 0,
+ .arg_count = 4,
},
{
- .name = "TopY",
- .desc = "Bounding rect top coordinate in tablet units . ",
- .prop_name = WACOM_PROP_TABLET_AREA,
- .prop_format = 32,
- .prop_offset = 1,
- },
- {
- .name = "BottomX",
- .desc = "Bounding rect right coordinate in tablet units. ",
- .prop_name = WACOM_PROP_TABLET_AREA,
- .prop_format = 32,
- .prop_offset = 2,
- },
- {
- .name = "BottomY",
- .desc = "Bounding rect bottom coordinate in tablet units. ",
- .prop_name = WACOM_PROP_TABLET_AREA,
- .prop_format = 32,
- .prop_offset = 3,
- },
- {
- .name = "Button1",
- .desc = "X11 event to which button 1 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button2",
- .desc = "X11 event to which button 2 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button3",
- .desc = "X11 event to which button 3 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button4",
- .desc = "X11 event to which button 4 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button5",
- .desc = "X11 event to which button 5 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button6",
- .desc = "X11 event to which button 6 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button7",
- .desc = "X11 event to which button 7 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button8",
- .desc = "X11 event to which button 8 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button9",
- .desc = "X11 event to which button 9 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button10",
- .desc = "X11 event to which button 10 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button11",
- .desc = "X11 event to which button 11 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button12",
- .desc = "X11 event to which button 12 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button13",
- .desc = "X11 event to which button 13 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button14",
- .desc = "X11 event to which button 14 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button15",
- .desc = "X11 event to which button 15 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button16",
- .desc = "X11 event to which button 16 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button17",
- .desc = "X11 event to which button 17 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button18",
- .desc = "X11 event to which button 18 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
+ .name = "Button",
+ .desc = "X11 event to which the given button should be mapped. ",
+ .prop_name = WACOM_PROP_BUTTON_ACTIONS,
+ .arg_count = 1,
+ .set_func = map_actions,
+ .get_func = get_map,
},
{
- .name = "Button19",
- .desc = "X11 event to which button 19 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button20",
- .desc = "X11 event to which button 20 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button21",
- .desc = "X11 event to which button 21 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button22",
- .desc = "X11 event to which button 22 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button23",
- .desc = "X11 event to which button 23 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button24",
- .desc = "X11 event to which button 24 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button25",
- .desc = "X11 event to which button 25 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button26",
- .desc = "X11 event to which button 26 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button27",
- .desc = "X11 event to which button 27 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button28",
- .desc = "X11 event to which button 28 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button29",
- .desc = "X11 event to which button 29 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button30",
- .desc = "X11 event to which button 30 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button31",
- .desc = "X11 event to which button 31 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "Button32",
- .desc = "X11 event to which button 32 should be mapped. ",
- .set_func = map_button,
- .get_func = get_button,
- },
- {
- .name = "DebugLevel",
- .desc = "Level of debugging trace for individual devices, "
- "default is 0 (off). ",
+ .name = "ToolDebugLevel",
+ .desc = "Level of debugging trace for individual tools "
+ "(default is 0 [off]). ",
.prop_name = WACOM_PROP_DEBUGLEVELS,
.prop_format = 8,
.prop_offset = 0,
+ .arg_count = 1,
},
{
- .name = "CommonDBG",
- .desc = "Level of debugging statements applied to all devices "
- "associated with the same tablet. default is 0 (off). ",
+ .name = "TabletDebugLevel",
+ .desc = "Level of debugging statements applied to shared "
+ "code paths between all tools "
+ "associated with the same tablet (default is 0 [off]). ",
.prop_name = WACOM_PROP_DEBUGLEVELS,
.prop_format = 8,
.prop_offset = 1,
+ .arg_count = 1,
},
{
.name = "Suppress",
- .desc = "Number of points trimmed, default is 2. ",
+ .desc = "Number of points trimmed (default is 2). ",
.prop_name = WACOM_PROP_SAMPLE,
.prop_format = 32,
- .prop_offset = 1,
+ .prop_offset = 0,
+ .arg_count = 1,
},
{
.name = "RawSample",
- .desc = "Number of raw data used to filter the points, "
- "default is 4. ",
+ .desc = "Number of raw data used to filter the points "
+ "(default is 4). ",
.prop_name = WACOM_PROP_SAMPLE,
.prop_format = 32,
- .prop_offset = 0,
- },
- {
- .name = "Screen_No",
- .desc = "Sets/gets screen number the tablet is mapped to, "
- "default is -1. ",
- .prop_name = WACOM_PROP_DISPLAY_OPTS,
- .prop_format = 8,
- .prop_offset = 0,
+ .prop_offset = 1,
+ .arg_count = 1,
},
{
- .name = "PressCurve",
- .desc = "Bezier curve for pressure (default is 0 0 100 100). ",
+ .name = "PressureCurve",
+ .desc = "Bezier curve for pressure (default is 0 0 100 100 [linear]). ",
.prop_name = WACOM_PROP_PRESSURECURVE,
.prop_format = 32,
.prop_offset = 0,
- .get_func = get_presscurve,
- },
- {
- .name = "TwinView",
- .desc = "Sets the mapping to TwinView horizontal/vertical/none. "
- "Values = none, vertical, horizontal (default is none).",
- .prop_name = WACOM_PROP_DISPLAY_OPTS,
- .prop_format = 8,
- .prop_offset = 1,
- .get_func = get_twinview,
- .set_func = set_twinview,
+ .arg_count = 4,
},
{
.name = "Mode",
- .desc = "Switches cursor movement mode (default is absolute/on). ",
+ .desc = "Switches cursor movement mode (default is absolute). ",
+ .arg_count = 1,
.set_func = set_mode,
.get_func = get_mode,
},
{
- .name = "TPCButton",
- .desc = "Turns on/off Tablet PC buttons. "
- "default is off for regular tablets, "
- "on for Tablet PC. ",
+ .name = "TabletPCButton",
+ .desc = "Turns on/off Tablet PC buttons "
+ "(default is off for regular tablets, "
+ "on for Tablet PC). ",
.prop_name = WACOM_PROP_HOVER,
.prop_format = 8,
.prop_offset = 0,
- .prop_flags = PROP_FLAG_BOOLEAN
+ .arg_count = 1,
+ .prop_flags = PROP_FLAG_BOOLEAN | PROP_FLAG_INVERTED
},
{
.name = "Touch",
- .desc = "Turns on/off Touch events (default is enable/on). ",
+ .desc = "Turns on/off Touch events (default is on). ",
.prop_name = WACOM_PROP_TOUCH,
.prop_format = 8,
.prop_offset = 0,
+ .arg_count = 1,
.prop_flags = PROP_FLAG_BOOLEAN
},
{
- .name = "Capacity",
- .desc = "Touch sensitivity level (default is 3, "
- "-1 for none capacitive tools).",
- .prop_name = WACOM_PROP_CAPACITY,
+ .name = "Gesture",
+ .desc = "Turns on/off multi-touch gesture events "
+ "(default is on). ",
+ .prop_name = WACOM_PROP_ENABLE_GESTURE,
+ .prop_format = 8,
+ .prop_offset = 0,
+ .arg_count = 1,
+ .prop_flags = PROP_FLAG_BOOLEAN
+ },
+ {
+ .name = "ZoomDistance",
+ .desc = "Minimum distance for a zoom gesture "
+ "(default is 50). ",
+ .prop_name = WACOM_PROP_GESTURE_PARAMETERS,
.prop_format = 32,
.prop_offset = 0,
+ .arg_count = 1,
+ },
+ {
+ .name = "ScrollDistance",
+ .desc = "Minimum motion before sending a scroll gesture "
+ "(default is 20). ",
+ .prop_name = WACOM_PROP_GESTURE_PARAMETERS,
+ .prop_format = 32,
+ .prop_offset = 1,
+ .arg_count = 1,
+ },
+ {
+ .name = "TapTime",
+ .desc = "Minimum time between taps for a right click "
+ "(default is 250). ",
+ .prop_name = WACOM_PROP_GESTURE_PARAMETERS,
+ .prop_format = 32,
+ .prop_offset = 2,
+ .arg_count = 1,
},
{
- .name = "CursorProx",
+ .name = "CursorProximity",
.desc = "Sets cursor distance for proximity-out "
- "in distance from the tablet. "
+ "in distance from the tablet "
"(default is 10 for Intuos series, "
- "42 for Graphire series).",
+ "42 for Graphire series). ",
.prop_name = WACOM_PROP_PROXIMITY_THRESHOLD,
.prop_format = 32,
.prop_offset = 0,
+ .arg_count = 1,
},
{
.name = "Rotate",
.desc = "Sets the rotation of the tablet. "
- "Values = NONE, CW, CCW, HALF (default is NONE).",
+ "Values = none, cw, ccw, half (default is none). ",
.prop_name = WACOM_PROP_ROTATION,
.set_func = set_rotate,
.get_func = get_rotate,
+ .arg_count = 1,
},
{
- .name = "RelWUp",
+ .name = "RelWheelUp",
.desc = "X11 event to which relative wheel up should be mapped. ",
.prop_name = WACOM_PROP_WHEELBUTTONS,
.prop_format = 8,
.prop_offset = 0,
+ .arg_count = 0,
+ .set_func = map_actions,
+ .get_func = get_map,
},
{
- .name = "RelWDn",
+ .name = "RelWheelDown",
.desc = "X11 event to which relative wheel down should be mapped. ",
.prop_name = WACOM_PROP_WHEELBUTTONS,
.prop_format = 8,
.prop_offset = 1,
+ .arg_count = 0,
+ .set_func = map_actions,
+ .get_func = get_map,
},
{
- .name = "AbsWUp",
+ .name = "AbsWheelUp",
.desc = "X11 event to which absolute wheel up should be mapped. ",
.prop_name = WACOM_PROP_WHEELBUTTONS,
.prop_format = 8,
.prop_offset = 2,
+ .arg_count = 0,
+ .set_func = map_actions,
+ .get_func = get_map,
},
{
- .name = "AbsWDn",
+ .name = "AbsWheelDown",
.desc = "X11 event to which absolute wheel down should be mapped. ",
.prop_name = WACOM_PROP_WHEELBUTTONS,
.prop_format = 8,
.prop_offset = 3,
+ .arg_count = 0,
+ .set_func = map_actions,
+ .get_func = get_map,
},
{
- .name = "StripLUp",
+ .name = "AbsWheel2Up",
+ .desc = "X11 event to which absolute wheel up should be mapped. ",
+ .prop_name = WACOM_PROP_WHEELBUTTONS,
+ .prop_format = 8,
+ .prop_offset = 4,
+ .arg_count = 0,
+ .set_func = map_actions,
+ .get_func = get_map,
+ },
+ {
+ .name = "AbsWheel2Down",
+ .desc = "X11 event to which absolute wheel down should be mapped. ",
+ .prop_name = WACOM_PROP_WHEELBUTTONS,
+ .prop_format = 8,
+ .prop_offset = 5,
+ .arg_count = 0,
+ .set_func = map_actions,
+ .get_func = get_map,
+ },
+ {
+ .name = "StripLeftUp",
.desc = "X11 event to which left strip up should be mapped. ",
.prop_name = WACOM_PROP_STRIPBUTTONS,
.prop_format = 8,
.prop_offset = 0,
+ .arg_count = 0,
+ .set_func = map_actions,
+ .get_func = get_map,
},
{
- .name = "StripLDn",
+ .name = "StripLeftDown",
.desc = "X11 event to which left strip down should be mapped. ",
.prop_name = WACOM_PROP_STRIPBUTTONS,
.prop_format = 8,
.prop_offset = 1,
+ .arg_count = 0,
+ .set_func = map_actions,
+ .get_func = get_map,
},
{
- .name = "StripRUp",
+ .name = "StripRightUp",
.desc = "X11 event to which right strip up should be mapped. ",
.prop_name = WACOM_PROP_STRIPBUTTONS,
.prop_format = 8,
.prop_offset = 2,
+ .arg_count = 0,
+ .set_func = map_actions,
+ .get_func = get_map,
},
{
- .name = "StripRDn",
+ .name = "StripRightDown",
.desc = "X11 event to which right strip down should be mapped. ",
.prop_name = WACOM_PROP_STRIPBUTTONS,
.prop_format = 8,
.prop_offset = 3,
+ .arg_count = 0,
+ .set_func = map_actions,
+ .get_func = get_map,
},
{
- .name = "TVResolution",
- .desc = "Sets MetaModes option for TwinView. ",
- .prop_name = WACOM_PROP_TWINVIEW_RES,
- .prop_format = 32,
- .prop_offset = 0,
- .prop_extra = 3,
- },
- {
- .name = "RawFilter",
- .desc = "Enables and disables filtering of raw data, "
- "default is true/on.",
- .prop_name = WACOM_PROP_SAMPLE,
- .prop_format = 8,
- .prop_offset = 0,
- .prop_flags = PROP_FLAG_BOOLEAN
- },
- {
- .name = "ClickForce",
- .desc = "Sets tip/eraser pressure threshold = ClickForce*MaxZ/100 "
- "(default is 6)",
+ .name = "Threshold",
+ .desc = "Sets tip/eraser pressure threshold "
+ "(default is 27). ",
.prop_name = WACOM_PROP_PRESSURE_THRESHOLD,
.prop_format = 32,
.prop_offset = 0,
+ .arg_count = 1,
},
{
- .name = "xyDefault",
+ .name = "ResetArea",
.desc = "Resets the bounding coordinates to default in tablet units. ",
.prop_name = WACOM_PROP_TABLET_AREA,
.prop_format = 32,
.prop_offset = 0,
+ .arg_count = 0,
.prop_flags = PROP_FLAG_WRITEONLY,
.set_func = set_xydefault,
},
{
- .name = "mmonitor",
- .desc = "Turns on/off across monitor movement in "
- "multi-monitor desktop, default is on ",
- .prop_name = WACOM_PROP_DISPLAY_OPTS,
- .prop_format = 8,
- .prop_offset = 2,
- },
- {
- .name = "STopX0",
- .desc = "Screen 0 left coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 0,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopY0",
- .desc = "Screen 0 top coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 1,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomX0",
- .desc = "Screen 0 right coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 2,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomY0",
- .desc = "Screen 0 bottom coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 3,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopX1",
- .desc = "Screen 1 left coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 4,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopY1",
- .desc = "Screen 1 top coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 5,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomX1",
- .desc = "Screen 1 right coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 6,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomY1",
- .desc = "Screen 1 bottom coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 7,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopX2",
- .desc = "Screen 2 left coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 8,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopY2",
- .desc = "Screen 2 top coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 9,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomX2",
- .desc = "Screen 2 right coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_offset = 10,
- .prop_format = 32,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomY2",
- .desc = "Screen 2 bottom coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 11,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopX3",
- .desc = "Screen 3 left coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 12,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopY3",
- .desc = "Screen 3 top coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 13,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomX3",
- .desc = "Screen 3 right coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 14,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomY3",
- .desc = "Screen 3 bottom coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 15,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopX4",
- .desc = "Screen 4 left coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 16,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopY4",
- .desc = "Screen 4 top coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 17,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomX4",
- .desc = "Screen 4 right coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 18,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomY4",
- .desc = "Screen 4 bottom coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 19,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopX5",
- .desc = "Screen 5 left coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 20,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopY5",
- .desc = "Screen 5 top coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 21,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomX5",
- .desc = "Screen 5 right coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 22,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomY5",
- .desc = "Screen 5 bottom coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 23,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopX6",
- .desc = "Screen 6 left coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 24,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopY6",
- .desc = "Screen 6 top coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 25,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomX6",
- .desc = "Screen 6 right coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 26,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomY6",
- .desc = "Screen 6 bottom coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 27,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopX7",
- .desc = "Screen 7 left coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 28,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "STopY7",
- "Screen 7 top coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 29,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomX7",
- .desc = "Screen 7 right coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 30,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
- .name = "SBottomY7",
- .desc = "Screen 7 bottom coordinate in pixels. ",
- .prop_name = WACOM_PROP_SCREENAREA,
- .prop_format = 32,
- .prop_offset = 31,
- .prop_flags = PROP_FLAG_READONLY
- },
- {
.name = "ToolID",
.desc = "Returns the ID of the associated device. ",
.prop_name = WACOM_PROP_TOOL_TYPE,
.prop_format = 32,
.prop_offset = 0,
+ .arg_count = 1,
.prop_flags = PROP_FLAG_READONLY
},
{
.name = "ToolSerial",
- .desc = "Returns the serial number of the associated device. ",
+ .desc = "Returns the serial number of the current device in proximity.",
.prop_name = WACOM_PROP_SERIALIDS,
.prop_format = 32,
.prop_offset = 3,
+ .arg_count = 1,
.prop_flags = PROP_FLAG_READONLY
},
{
- .name = "TabletID",
- .desc = "Returns the tablet ID of the associated device. ",
+ .name = "ToolSerialPrevious",
+ .desc = "Returns the serial number of the previous device in proximity.",
.prop_name = WACOM_PROP_SERIALIDS,
.prop_format = 32,
- .prop_offset = 0,
+ .prop_offset = 1,
+ .arg_count = 1,
.prop_flags = PROP_FLAG_READONLY
},
{
- .name = "GetTabletID",
+ .name = "BindToSerial",
+ .desc = "Binds this device to the serial number.",
+ .prop_name = WACOM_PROP_SERIAL_BIND,
+ .prop_format = 32,
+ .prop_offset = 0,
+ .arg_count = 1,
+ },
+ {
+ .name = "TabletID",
.desc = "Returns the tablet ID of the associated device. ",
.prop_name = WACOM_PROP_SERIALIDS,
.prop_format = 32,
.prop_offset = 0,
+ .arg_count = 1,
.prop_flags = PROP_FLAG_READONLY
},
{
- .name = "NumScreen",
- .desc = "Returns number of screens configured for the desktop. ",
- .set_func = not_implemented,
- .get_func = not_implemented,
- },
- {
- .name = "XScaling",
- .desc = "Returns the status of XSCALING is set or not. ",
- .set_func = not_implemented,
- .get_func = not_implemented,
+ .name = "MapToOutput",
+ .desc = "Map the device to the given output. ",
+ .set_func = set_output,
+ .arg_count = 1,
+ .prop_flags = PROP_FLAG_WRITEONLY | PROP_FLAG_OUTPUT,
},
{
.name = "all",
- .desc = "Get value for all parameters.",
+ .desc = "Get value for all parameters. ",
.get_func = get_all,
.prop_flags = PROP_FLAG_READONLY,
},
{ NULL }
};
+/**
+ * Deprecated parameters and their respective replacements.
+ */
+struct deprecated
+{
+ const char *name;
+ const char *replacement;
+} deprecated_parameters[] =
+{
+ {"Button", "Button"}, /* this covers Button1-32 */
+ {"TopX", "Area"},
+ {"TopY", "Area"},
+ {"BottomX", "Area"},
+ {"BottomY", "Area"},
+ {"GetTabletID", "TabletID"},
+ {"DebugLevel", "ToolDebugLevel"},
+ {"CommonDBG", "TabletDebugLevel"},
+ {"GetTabletID", "TabletID"},
+ {"PressCurve", "PressureCurve"},
+ {"TPCButton", "TabletPCButton"},
+ {"CursorProx", "CursorProximity"},
+ {"xyDefault", "ResetArea"},
+ {"ClickForce", "Threshold"},
+ {"RawFilter", NULL},
+ {"Capacity", NULL},
+ {NULL, NULL}
+};
+
+/**
+ * Check if name is deprecated and print out a warning if it is.
+ *
+ * @return True if deprecated, False otherwise.
+ */
+static Bool
+is_deprecated_parameter(const char *name)
+{
+ struct deprecated *d;
+ Bool is_deprecated = False;
+
+ /* all others */
+ for (d = deprecated_parameters; d->name; d++)
+ {
+ if (strncmp(name, d->name, strlen(d->name)) == 0)
+ {
+ is_deprecated = True;
+ break;
+ }
+ }
+
+ if (is_deprecated)
+ {
+ printf("Parameter '%s' is no longer in use. ", name);
+ if (d->replacement != NULL)
+ printf("It was replaced with '%s'.\n", d->replacement);
+ else
+ printf("Its use has been deprecated.\n");
+ }
+
+ return is_deprecated;
+
+}
+
+struct modifier {
+ char *name;
+ char *converted;
+};
+
+static struct modifier modifiers[] = {
+ {"ctrl", "Control_L"},
+ {"ctl", "Control_L"},
+ {"control", "Control_L"},
+ {"lctrl", "Control_L"},
+ {"rctrl", "Control_R"},
+
+ {"meta", "Meta_L"},
+ {"lmeta", "Meta_L"},
+ {"rmeta", "Meta_R"},
+
+ {"alt", "Alt_L"},
+ {"lalt", "Alt_L"},
+ {"ralt", "Alt_R"},
+
+ {"shift", "Shift_L"},
+ {"lshift", "Shift_L"},
+ {"rshift", "Shift_R"},
+
+ {"super", "Super_L"},
+ {"lsuper", "Super_L"},
+ {"rsuper", "Super_R"},
+
+ {"hyper", "Hyper_L"},
+ {"lhyper", "Hyper_L"},
+ {"rhyper", "Hyper_R"},
+
+ { NULL, NULL }
+};
+
+static struct modifier specialkeys[] = {
+ {"f1", "F1"}, {"f2", "F2"}, {"f3", "F3"},
+ {"f4", "F4"}, {"f5", "F5"}, {"f6", "F6"},
+ {"f7", "F7"}, {"f8", "F8"}, {"f9", "F9"},
+ {"f10", "F10"}, {"f11", "F11"}, {"f12", "F12"},
+ {"f13", "F13"}, {"f14", "F14"}, {"f15", "F15"},
+ {"f16", "F16"}, {"f17", "F17"}, {"f18", "F18"},
+ {"f19", "F19"}, {"f20", "F20"}, {"f21", "F21"},
+ {"f22", "F22"}, {"f23", "F23"}, {"f24", "F24"},
+ {"f25", "F25"}, {"f26", "F26"}, {"f27", "F27"},
+ {"f28", "F28"}, {"f29", "F29"}, {"f30", "F30"},
+ {"f31", "F31"}, {"f32", "F32"}, {"f33", "F33"},
+ {"f34", "F34"}, {"f35", "F35"},
+
+ {"esc", "Escape"}, {"Esc", "Escape"},
+
+ {"up", "Up"}, {"down", "Down"},
+ {"left", "Left"}, {"right", "Right"},
+
+ {"backspace", "BackSpace"}, {"Backspace", "BackSpace"},
+
+ {"tab", "Tab"},
+
+ {"PgUp", "Prior"}, {"PgDn", "Next"},
+
+ { NULL, NULL }
+};
+
static param_t* find_parameter(char *name)
{
param_t *param = NULL;
@@ -867,16 +608,17 @@ static void usage(void)
" -h, --help - usage\n"
" -v, --verbose - verbose output\n"
" -V, --version - version info\n"
- " -d, --display disp_name - override default display\n"
+ " -d, --display \"display\" - override default display\n"
" -s, --shell - generate shell commands for 'get'\n"
- " -x, --xconf - generate X.conf lines for 'get'\n");
+ " -x, --xconf - generate xorg.conf lines for 'get'\n");
printf(
"\nCommands:\n"
- " --list [dev|param] - display known devices, parameters \n"
- " --list mod - display supported modifier and specific keys for keystokes [not implemented}\n"
- " --set dev_name param [values...] - set device parameter by name\n"
- " --get dev_name param [param...] - get current device parameter(s) value by name\n");
+ " --list devices - display detected devices\n"
+ " --list parameters - display supported parameters\n"
+ " --list modifiers - display supported modifier and specific keys for keystrokes\n"
+ " --set \"device name\" parameter [values...] - set device parameter by name\n"
+ " --get \"device name\" parameter [param...] - get current device parameter(s) value by name\n");
}
@@ -941,6 +683,32 @@ static XDevice* find_device(Display *display, char *name)
return dev;
}
+/* Return True if the given device has the property, or False otherwise */
+static Bool test_property(Display *dpy, XDevice* dev, Atom prop)
+{
+ int nprops_return;
+ Atom *properties;
+ int found = False;
+
+ /* if no property is required, return success */
+ if (prop == None)
+ return True;
+
+ properties = XListDeviceProperties(dpy, dev, &nprops_return);
+
+ while(nprops_return--)
+ {
+ if (properties[nprops_return] == prop)
+ {
+ found = True;
+ break;
+ }
+ }
+
+ XFree(properties);
+ return found;
+}
+
static void list_one_device(Display *dpy, XDeviceInfo *info)
{
static int wacom_prop = 0;
@@ -978,7 +746,9 @@ static void list_one_device(Display *dpy, XDeviceInfo *info)
if (nitems)
{
type_name = XGetAtomName(dpy, *(Atom*)data);
- printf("%-16s %-10s\n", info->name, type_name);
+ printf("%-32s id: %ld type: %-10s\n",
+ info->name, info->id,
+ type_name);
}
XFree(data);
@@ -1025,156 +795,91 @@ static void list_param(Display *dpy)
while(param->name)
{
- printf("%-16s - %16s%s\n", param->name, param->desc,
- (param->set_func == not_implemented) ? " [not implemented]" : "");
+ printf("%-16s - %16s\n", param->name, param->desc);
param++;
}
}
+static void list_mod(Display *dpy)
+{
+ struct modifier *m = modifiers;
+
+ printf("%zd modifiers are supported:\n", ARRAY_SIZE(modifiers) - 1);
+ while(m->name)
+ printf(" %s\n", m++->name);
+
+ printf("\n%zd specialkeys are supported:\n", ARRAY_SIZE(specialkeys) - 1);
+ m = specialkeys;
+ while(m->name)
+ printf(" %s\n", m++->name);
+}
+
static void list(Display *dpy, int argc, char **argv)
{
TRACE("'list' requested.\n");
if (argc == 0)
list_devices(dpy);
- else if (strcmp(argv[0], "dev") == 0)
+ else if (strcmp(argv[0], "dev") == 0 ||
+ strcmp(argv[0], "devices") == 0)
list_devices(dpy);
- else if (strcmp(argv[0], "param") == 0)
+ else if (strcmp(argv[0], "param") == 0 ||
+ strcmp(argv[0], "parameters") == 0)
list_param(dpy);
+ else if (strcmp(argv[0], "mod") == 0 ||
+ strcmp(argv[0], "modifiers") == 0)
+ list_mod(dpy);
else
printf("unknown argument to list.\n");
}
-/*
+/**
* Convert a list of random special keys to strings that can be passed into
* XStringToKeysym
+ * @param special A special key, e.g. a modifier or one of the keys in
+ * specialkeys.
+ * @return The X Keysym representing specialkey.
*/
-static char *convert_specialkey(const char *modifier)
+static char *convert_specialkey(const char *specialkey)
{
- struct modifier {
- char *name;
- char *converted;
- } modmap[] = {
- {"ctrl", "Control_L"},
- {"ctl", "Control_L"},
- {"control", "Control_L"},
- {"lctrl", "Control_L"},
- {"rctrl", "Control_R"},
-
- {"meta", "Meta_L"},
- {"lmeta", "Meta_L"},
- {"rmeta", "Meta_R"},
-
- {"alt", "Alt_L"},
- {"lalt", "Alt_L"},
- {"ralt", "Alt_R"},
-
- {"shift", "Shift_L"},
- {"lshift", "Shift_L"},
- {"rshift", "Shift_R"},
-
- {"super", "Super_L"},
- {"lsuper", "Super_L"},
- {"rsuper", "Super_R"},
-
- {"hyper", "Hyper_L"},
- {"lhyper", "Hyper_L"},
- {"rhyper", "Hyper_R"},
-
- {"f1", "F1"}, {"f2", "F2"}, {"f3", "F3"},
- {"f4", "F4"}, {"f5", "F5"}, {"f6", "F6"},
- {"f7", "F7"}, {"f8", "F8"}, {"f9", "F9"},
- {"f10", "F10"}, {"f11", "F11"}, {"f12", "F12"},
- {"f13", "F13"}, {"f14", "F14"}, {"f15", "F15"},
- {"f16", "F16"}, {"f17", "F17"}, {"f18", "F18"},
- {"f19", "F19"}, {"f20", "F20"}, {"f21", "F21"},
- {"f22", "F22"}, {"f23", "F23"}, {"f24", "F24"},
- {"f25", "F25"}, {"f26", "F26"}, {"f27", "F27"},
- {"f28", "F28"}, {"f29", "F29"}, {"f30", "F30"},
- {"f31", "F31"}, {"f32", "F32"}, {"f33", "F33"},
- {"f34", "F34"}, {"f35", "F35"},
-
- {"esc", "Escape"}, {"Esc", "Escape"},
-
- {"up", "Up"}, {"down", "Down"},
- {"left", "Left"}, {"right", "Right"},
-
- {"backspace", "BackSpace"}, {"Backspace", "BackSpace"},
-
- { NULL, NULL }
- };
+ struct modifier *m = modifiers;
- struct modifier *m = modmap;
-
- while(m->name && strcasecmp(modifier, m->name))
+ while(m->name && strcasecmp(specialkey, m->name))
m++;
- return m->converted ? m->converted : (char*)modifier;
-}
-
-static int is_modifier(const char* modifier)
-{
- const char *modifiers[] = {
- "Control_L",
- "Control_R",
- "Alt_L",
- "Alt_R",
- "Shift_L",
- "Shift_R",
- "Meta_L",
- "Meta_R",
- NULL,
- };
-
- const char **m = modifiers;
-
- while(*m)
+ if (!m->name)
{
- if (strcmp(modifier, *m) == 0)
- return 1;
- m++;
+ m = specialkeys;
+ while(m->name && strcasecmp(specialkey, m->name))
+ m++;
}
- return 0;
+ return m->converted ? m->converted : (char*)specialkey;
}
-/* Return the first keycode to have the required keysym in the current group.
- TODOs:
- - parse other groups as well (do we need this?)
- - for keysyms not on level 0, return the keycodes for the modifiers as
- well
-*/
-static int keysym_to_keycode(Display *dpy, KeySym sym)
+/**
+ * @param keysym An X Keysym
+ * @return nonzero if the given keysym is a modifier (as per the modifiers
+ * list) or zero otherwise.
+ */
+static int is_modifier(const char* keysym)
{
- static XkbDescPtr xkb = NULL;
- XkbStateRec state;
- int group;
- int kc = 0;
-
-
- if (!xkb)
- xkb = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
- XkbGetState(dpy, XkbUseCoreKbd, &state);
- group = state.group;
+ struct modifier *m = modifiers;
- for (kc = xkb->min_key_code; kc <= xkb->max_key_code; kc++)
+ while(m->name)
{
- KeySym* ks;
- int i;
-
- ks = XkbKeySymsPtr(xkb, kc);
- for (i = 0; i < XkbKeyGroupWidth(xkb, kc, state.group); i++)
- if (ks[i] == sym)
- goto out;
+ if (strcmp(keysym, m->converted) == 0)
+ break;
+ m++;
}
-out:
- return kc;
+ return (m->name != NULL);
}
-static int special_map_keystrokes(Display*, int argc, char **argv, unsigned long *ndata, unsigned long* data);
-static int special_map_button(Display*, int argc, char **argv, unsigned long *ndata, unsigned long* data);
-static int special_map_core(Display*, int argc, char **argv, unsigned long *ndata, unsigned long *data);
-static int special_map_modetoggle(Display*, int argc, char **argv, unsigned long *ndata, unsigned long *data);
-static int special_map_displaytoggle(Display*, int argc, char **argv, unsigned long *ndata, unsigned long *data);
+
+static int special_map_keystrokes(Display *dpy, int argc, char **argv, unsigned long *ndata, unsigned long* data);
+static int special_map_button(Display *dpy, int argc, char **argv, unsigned long *ndata, unsigned long* data);
+static int special_map_core(Display *dpy, int argc, char **argv, unsigned long *ndata, unsigned long *data);
+static int special_map_modetoggle(Display *dpy, int argc, char **argv, unsigned long *ndata, unsigned long *data);
+static int special_map_displaytoggle(Display *dpy, int argc, char **argv, unsigned long *ndata, unsigned long *data);
/* Valid keywords for the --set ButtonX options */
struct keywords {
@@ -1203,7 +908,7 @@ static int special_map_core(Display *dpy, int argc, char **argv, unsigned long *
return 0;
}
-static int special_map_modetoggle(Display* dpy, int argc, char **argv, unsigned long *ndata, unsigned long *data)
+static int special_map_modetoggle(Display *dpy, int argc, char **argv, unsigned long *ndata, unsigned long *data)
{
data[*ndata] = AC_MODETOGGLE;
@@ -1212,7 +917,7 @@ static int special_map_modetoggle(Display* dpy, int argc, char **argv, unsigned
return 0;
}
-static int special_map_displaytoggle(Display* dpy, int argc, char **argv, unsigned long *ndata, unsigned long *data)
+static int special_map_displaytoggle(Display *dpy, int argc, char **argv, unsigned long *ndata, unsigned long *data)
{
data[*ndata] = AC_DISPLAYTOGGLE;
@@ -1234,7 +939,7 @@ static inline int is_valid_keyword(const char *keyword)
return 0;
}
-static int special_map_button(Display* dpy, int argc, char **argv, unsigned long *ndata, unsigned long *data)
+static int special_map_button(Display *dpy, int argc, char **argv, unsigned long *ndata, unsigned long *data)
{
int nitems = 0;
int i;
@@ -1279,6 +984,37 @@ static int special_map_button(Display* dpy, int argc, char **argv, unsigned long
return nitems;
}
+/* Return the first keycode to have the required keysym in the current group.
+ TODOs:
+ - parse other groups as well (do we need this?)
+ - for keysyms not on level 0, return the keycodes for the modifiers as
+ well
+*/
+static int keysym_to_keycode(Display *dpy, KeySym sym)
+{
+ static XkbDescPtr xkb = NULL;
+ XkbStateRec state;
+ int kc = 0;
+
+
+ if (!xkb)
+ xkb = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
+ XkbGetState(dpy, XkbUseCoreKbd, &state);
+
+ for (kc = xkb->min_key_code; kc <= xkb->max_key_code; kc++)
+ {
+ KeySym* ks;
+ int i;
+
+ ks = XkbKeySymsPtr(xkb, kc);
+ for (i = 0; i < XkbKeyGroupWidth(xkb, kc, state.group); i++)
+ if (ks[i] == sym)
+ goto out;
+ }
+
+out:
+ return kc;
+}
/*
Map gibberish like "ctrl alt f2" into the matching AC_KEY values.
Returns 1 on success or 0 otherwise.
@@ -1389,70 +1125,52 @@ static char** strjoinsplit(int argc, char **argv, int *nwords)
return words;
}
-static int get_button_number_from_string(const char* string)
-{
- int slen = strlen("Button");
- if (slen >= strlen(string) || strncasecmp(string, "Button", slen))
- return -1;
- return atoi(&string[strlen("Button")]);
-}
-
-/* Handles complex button mappings through button actions. */
-static void special_map_buttons(Display *dpy, XDevice *dev, param_t* param, int argc, char **argv)
+/**
+ * This function parses the given strings to produce a list of actions that
+ * the driver can carry out. We first combine the strings and then split
+ * on spaces to produce a wordlist. Begining with the first word, we let each
+ * registered keyword parser try to parse the string; if one succeeds in
+ * parsing a portion, we jump ahead to the first word it could not parse
+ * and repeat the process. Each parser builds up the list of actions with
+ * those commands it can interpret.
+ *
+ * @param dpy X11 display to query
+ * @param argc Length of argv
+ * @param argv String data to be parsed
+ * @param data Parsed action data
+ * @return 'true' if the whole string was parsed sucessfully, else 'false'
+ */
+static Bool parse_actions(Display *dpy, int argc, char **argv, unsigned long* data, unsigned long *nitems)
{
- Atom btnact_prop, prop;
- unsigned long *data, *btnact_data;
- int slen = strlen("Button");
- int btn_no;
- Atom type;
- int format;
- unsigned long btnact_nitems, nitems, bytes_after;
- int need_update = 0;
- int i;
- int nwords = 0;
+ int i = 0;
+ int nwords = 0;
char **words = NULL;
- TRACE("Special %s map for device %ld.\n", param->name, dev->device_id);
-
- if (slen >= strlen(param->name) || strncmp(param->name, "Button", slen))
- return;
-
- btnact_prop = XInternAtom(dpy, "Wacom Button Actions", True);
- if (!btnact_prop)
- return;
-
- btn_no = get_button_number_from_string(param->name);
- btn_no--; /* property is zero-indexed, button numbers are 1-indexed */
-
- XGetDeviceProperty(dpy, dev, btnact_prop, 0, 100, False,
- AnyPropertyType, &type, &format, &btnact_nitems,
- &bytes_after, (unsigned char**)&btnact_data);
+ /* translate cmdline commands */
+ words = strjoinsplit(argc, argv, &nwords);
- if (btn_no > btnact_nitems)
- return;
+ if (nwords==1 && sscanf(words[0], "%d", &i) == 1)
+ { /* Mangle "simple" button maps into proper actions */
+ char **new_words = realloc(words, 2);
+ if (new_words == NULL)
+ {
+ fprintf(stderr, "Unable to reallocate memory.\n");
+ return False;
+ }
- if (argc == 0) /* unset property */
- btnact_data[btn_no] = 0;
- else if (btnact_data[btn_no]) /* some atom already assigned, modify that */
- prop = btnact_data[btn_no];
- else
- {
- char buff[64];
- sprintf(buff, "Wacom button action %d", (btn_no + 1));
- prop = XInternAtom(dpy, buff, False);
+ sprintf(new_words[0], "+%d", i);
+ new_words[1] = new_words[0];
+ new_words[0] = "button";
- btnact_data[btn_no] = prop;
- need_update = 1;
+ words = new_words;
+ nwords = 2;
}
- data = calloc(sizeof(long), 256);
- nitems = 0;
-
- /* translate cmdline commands */
- words = strjoinsplit(argc, argv, &nwords);
for (i = 0; i < nwords; i++)
{
int j = 0;
+ int keyword_found = 0;
+
while (keywords[j].keyword && i < nwords)
{
int parsed = 0;
@@ -1460,75 +1178,166 @@ static void special_map_buttons(Display *dpy, XDevice *dev, param_t* param, int
{
parsed = keywords[j].func(dpy, nwords - i - 1,
&words[i + 1],
- &nitems, data);
+ nitems, data);
i += parsed;
+ keyword_found = 1;
}
if (parsed)
j = parsed = 0; /* restart with first keyword */
else
j++;
}
+
+ if (!keyword_found)
+ {
+ fprintf(stderr, "Cannot parse keyword '%s' at position %d\n", words[i], i+1);
+ return False;
+ }
+ }
+
+ free(words);
+
+ return True;
+}
+
+/**
+ * Maps sub-properties (e.g. the 3rd button in WACOM_PROP_BUTTON_ACTIONS)
+ * to actions. This function leverages the several available parsing
+ * functions to convert plain-text descriptions into a list of actions
+ * the driver can understand.
+ *
+ * Once we have a list of actions, we can store it in the appropriate
+ * child property. If none exists, we must first create one and update
+ * the parent list. If we want no action to occur, we can delete the
+ * child property and have the parent point to '0' instead.
+ *
+ * @param dpy X display we want to query
+ * @param dev X device we want to modify
+ * @param btnact_prop Parent property
+ * @param offset Offset into the parent's list of child properties
+ * @param argc Number of command line arguments we've been passed
+ * @param argv Command line arguments we need to parse
+ */
+static void special_map_property(Display *dpy, XDevice *dev, Atom btnact_prop, int offset, int argc, char **argv)
+{
+ unsigned long *data, *btnact_data;
+ Atom type, prop = 0;
+ int format;
+ unsigned long btnact_nitems, bytes_after;
+ unsigned long nitems = 0;
+
+ data = calloc(256, sizeof(long));
+ if (!parse_actions(dpy, argc, argv, data, &nitems))
+ goto out;
+
+ /* obtain the button actions Atom */
+ XGetDeviceProperty(dpy, dev, btnact_prop, 0, 100, False,
+ AnyPropertyType, &type, &format, &btnact_nitems,
+ &bytes_after, (unsigned char**)&btnact_data);
+
+ if (offset > btnact_nitems)
+ {
+ fprintf(stderr, "Invalid offset into %s property.\n", XGetAtomName(dpy, btnact_prop));
+ goto out;
}
- if (argc > 0) /* unset property */
+ if (format != 32 || type != XA_ATOM)
+ {
+ fprintf(stderr, "Property '%s' in an unexpected format. This is a bug.\n",
+ XGetAtomName(dpy, btnact_prop));
+ goto out;
+ }
+
+ /* set or unset the property */
+ prop = btnact_data[offset];
+ if (nitems > 0)
+ { /* Setting a new or existing property */
+ if (!prop)
+ {
+ char buff[64];
+ sprintf(buff, "Wacom button action %d", (offset + 1));
+ prop = XInternAtom(dpy, buff, False);
+ btnact_data[offset] = prop;
+ }
+
+ /* FIXME: the property containing the key sequence must be
+ * set before updating the button action properties */
XChangeDeviceProperty(dpy, dev, prop, XA_INTEGER, 32,
PropModeReplace,
(unsigned char*)data, nitems);
- XChangeDeviceProperty(dpy, dev, btnact_prop, XA_ATOM, 32,
- PropModeReplace,
- (unsigned char*)btnact_data,
- btnact_nitems);
+ XChangeDeviceProperty(dpy, dev, btnact_prop, XA_ATOM, 32,
+ PropModeReplace,
+ (unsigned char*)btnact_data,
+ btnact_nitems);
+ }
+ else if (prop)
+ { /* Unsetting a property that exists */
+ btnact_data[offset] = 0;
+
+ XChangeDeviceProperty(dpy, dev, btnact_prop, XA_ATOM, 32,
+ PropModeReplace,
+ (unsigned char*)btnact_data,
+ btnact_nitems);
+
+ XDeleteDeviceProperty(dpy, dev, prop);
+ }
+
XFlush(dpy);
+out:
free(data);
}
-
-static void map_button_simple(Display *dpy, XDevice *dev, param_t* param, int button)
+/**
+ * Maps "actions" to certain properties. Actions allow for complex tasks to
+ * be performed when the driver recieves certain events. For example you
+ * could have an action of "key +alt f2" to open the run-application dialog
+ * in Gnome, or "button 4 4 4 4 4" to have applications scroll by 5 lines
+ * instead of 1.
+ *
+ * Buttons, wheels, and strips all support actions. Note that button actions
+ * require the button to modify as the first argument to this function.
+ *
+ * @param dpy X11 display to query
+ * @param dev Device to modify
+ * @param param Info about parameter to modify
+ * @param argc Size of argv
+ * @param argv Arguments to parse
+ */
+static void map_actions(Display *dpy, XDevice *dev, param_t* param, int argc, char **argv)
{
- int nmap = 256;
- unsigned char map[nmap];
- int btn_no = 0;
+ Atom action_prop;
+ int offset = param->prop_offset;
- btn_no = get_button_number_from_string(param->name);
- if (btn_no <= 0)
- return;
+ TRACE("Mapping %s for device %ld.\n", param->name, dev->device_id);
- nmap = XGetDeviceButtonMapping(dpy, dev, map, nmap);
- if (btn_no > nmap)
+ action_prop = XInternAtom(dpy, param->prop_name, True);
+ if (!action_prop)
{
- fprintf(stderr, "Button number does not exist on device.\n");
+ fprintf(stderr, "Unable to locate property '%s'\n", param->prop_name);
return;
}
- map[btn_no - 1] = button;
- XSetDeviceButtonMapping(dpy, dev, map, nmap);
- XFlush(dpy);
-
- /* If there's a property set, unset it */
- special_map_buttons(dpy, dev, param, 0, NULL);
-}
-/*
- Supports three variations.
- xsetwacom set device Button1 1
- - maps button 1 to logical button 1
- xsetwacom set device Button1 "key a b c d"
- - maps button 1 to key events a b c d
- */
-static void map_button(Display *dpy, XDevice *dev, param_t* param, int argc, char **argv)
-{
- int button;
-
- if (argc <= 0)
+ if (argc < param->arg_count)
+ {
+ fprintf(stderr, "Too few arguments provided.\n");
return;
+ }
- TRACE("Mapping %s for device %ld.\n", param->name, dev->device_id);
+ if (strcmp(param->prop_name, WACOM_PROP_BUTTON_ACTIONS) == 0)
+ {
+ if (sscanf(argv[0], "%d", &offset) != 1)
+ {
+ fprintf(stderr, "'%s' is not a valid button number.\n", argv[0]);
+ return;
+ }
- /* --set "device" Button1 3 */
- if (sscanf(argv[0], "%d", &button) == 1)
- map_button_simple(dpy, dev, param, button);
- else
- special_map_buttons(dpy, dev, param, argc, argv);
+ offset--; /* Property is 0-indexed, X buttons are 1-indexed */
+ argc--; /* Trim off the target button argument */
+ argv = &argv[1]; /* ... ditto ... */
+ }
+
+ special_map_property(dpy, dev, action_prop, offset, argc, argv);
}
static void set_xydefault(Display *dpy, XDevice *dev, param_t* param, int argc, char **argv)
@@ -1539,6 +1348,13 @@ static void set_xydefault(Display *dpy, XDevice *dev, param_t* param, int argc,
unsigned long nitems, bytes_after;
long *ldata;
+ if (argc != param->arg_count)
+ {
+ fprintf(stderr, "'%s' requires exactly %d value(s).\n", param->name,
+ param->arg_count);
+ return;
+ }
+
prop = XInternAtom(dpy, param->prop_name, True);
if (!prop)
{
@@ -1572,9 +1388,11 @@ out:
static void set_mode(Display *dpy, XDevice *dev, param_t* param, int argc, char **argv)
{
int mode = Absolute;
- if (argc < 1)
+
+ if (argc != param->arg_count)
{
- usage();
+ fprintf(stderr, "'%s' requires exactly %d value(s).\n", param->name,
+ param->arg_count);
return;
}
@@ -1603,21 +1421,29 @@ static void set_rotate(Display *dpy, XDevice *dev, param_t* param, int argc, cha
unsigned char* data;
unsigned long nitems, bytes_after;
- if (argc != 1)
- goto error;
+ if (argc != param->arg_count)
+ {
+ fprintf(stderr, "'%s' requires exactly %d value(s).\n", param->name,
+ param->arg_count);
+ return;
+ }
TRACE("Rotate '%s' for device %ld.\n", argv[0], dev->device_id);
- if (strcasecmp(argv[0], "CW") == 0)
+ if (strcasecmp(argv[0], "cw") == 0 || strcasecmp(argv[0], "1") == 0)
rotation = 1;
- else if (strcasecmp(argv[0], "CCW") == 0)
+ else if (strcasecmp(argv[0], "ccw") == 0 || strcasecmp(argv[0], "2") == 0)
rotation = 2;
- else if (strcasecmp(argv[0], "HALF") == 0)
+ else if (strcasecmp(argv[0], "half") == 0 || strcasecmp(argv[0], "3") == 0)
rotation = 3;
- else if (strcasecmp(argv[0], "NONE") == 0)
+ else if (strcasecmp(argv[0], "none") == 0 || strcasecmp(argv[0], "0") == 0)
rotation = 0;
else
- goto error;
+ {
+ fprintf(stderr, "'%s' is not a valid value for the '%s' property.\n",
+ argv[0], param->name);
+ return;
+ }
prop = XInternAtom(dpy, param->prop_name, True);
if (!prop)
@@ -1643,144 +1469,73 @@ static void set_rotate(Display *dpy, XDevice *dev, param_t* param, int argc, cha
XFlush(dpy);
return;
-
-error:
- fprintf(stderr, "Usage: xsetwacom <device name> Rotate [NONE | CW | CCW | HALF]\n");
- return;
}
-static void set_twinview(Display *dpy, XDevice *dev, param_t* param, int argc, char **argv)
-{
- int twinview = 0;
- Atom prop, type;
- int format;
- unsigned char* data;
- unsigned long nitems, bytes_after;
- unsigned long *tvdata;
-
- if (argc != 1)
- goto error;
-
- TRACE("TwinView '%s' for device %ld.\n", argv[0], dev->device_id);
-
- if (strcasecmp(argv[0], "none") == 0)
- twinview = TV_NONE;
- else if (strcasecmp(argv[0], "horizontal") == 0)
- twinview = TV_LEFT_RIGHT;
- else if (strcasecmp(argv[0], "vertical") == 0)
- twinview = TV_ABOVE_BELOW;
- else if (strcasecmp(argv[0], "belowof") == 0)
- twinview = TV_ABOVE_BELOW;
- else if (strcasecmp(argv[0], "aboveof") == 0)
- twinview = TV_BELOW_ABOVE;
- else if (strcasecmp(argv[0], "rightof") == 0)
- twinview = TV_LEFT_RIGHT;
- else if (strcasecmp(argv[0], "leftof") == 0)
- twinview = TV_RIGHT_LEFT;
- else
- goto error;
- prop = XInternAtom(dpy, param->prop_name, True);
- if (!prop)
+/**
+ * Performs intelligent string->int conversion. In addition to converting strings
+ * of digits into their corresponding integer values, it converts special string
+ * constants such as "off" or "false" (0) and "on" or "true" (1).
+ *
+ * The caller is expected to allocate and free memory for return_value.
+ *
+ * @param param the property paramaters
+ * @param value the string to be converted
+ * @param[out] return_value the integer representation of the 'value' parameter
+ * @return TRUE if the conversion succeeded, FALSE otherwise
+ */
+static Bool convert_value_from_user(const param_t *param, const char *value, int *return_value)
+{
+ if (param->prop_flags & PROP_FLAG_BOOLEAN)
{
- fprintf(stderr, "Property for '%s' not available.\n",
- param->name);
- return;
- }
-
- XGetDeviceProperty(dpy, dev, prop, 0, 1000, False, AnyPropertyType,
- &type, &format, &nitems, &bytes_after, &data);
+ if (strcasecmp(value, "off") == 0 || strcasecmp(value, "false") == 0)
+ *return_value = 0;
+ else if (strcasecmp(value, "on") == 0 || strcasecmp(value, "true") == 0)
+ *return_value = 1;
+ else
+ return False;
- if (nitems == 0 || format != 8)
- {
- fprintf(stderr, "Property for '%s' has no or wrong value - this is a bug.\n",
- param->name);
- return;
+ if (param->prop_flags & PROP_FLAG_INVERTED)
+ *return_value = !(*return_value);
}
+ else if (param->prop_flags & PROP_FLAG_OUTPUT)
+ {
+ const char *prefix = "HEAD-";
+ /* We currently support HEAD-X, where X is 0-9 */
+ if (strlen(value) != strlen(prefix) + 1 ||
+ strncasecmp(value, prefix, strlen(prefix)) != 0)
+ return False;
- data[param->prop_offset] = twinview;
- XChangeDeviceProperty(dpy, dev, prop, type, format,
- PropModeReplace, data, nitems);
-
- /* set the TVResolution to some sane defaults */
- prop = XInternAtom(dpy, WACOM_PROP_TWINVIEW_RES, True);
- XGetDeviceProperty(dpy, dev, prop, 0, 1000, False, AnyPropertyType,
- &type, &format, &nitems, &bytes_after, &data);
- if (nitems != 4 || format != 32)
+ *return_value = value[strlen(prefix)] - '0';
+ } else
{
- fprintf(stderr, "Property for '%s' has no or wrong value - this is a bug.\n",
- WACOM_PROP_TWINVIEW_RES);
- return;
- }
+ char *end;
+ long conversion = strtol(value, &end, 10);
+ if (end == value || *end != '\0' || errno == ERANGE ||
+ conversion < INT_MIN || conversion > INT_MAX)
+ return False;
- tvdata = (unsigned long*)data;
- switch(twinview) {
- case TV_NONE:
- tvdata[0] = 0;
- tvdata[1] = 0;
- tvdata[2] = 0;
- tvdata[3] = 0;
- break;
- case TV_LEFT_RIGHT:
- case TV_RIGHT_LEFT:
- tvdata[0] = DisplayWidth(dpy, 0)/2;
- tvdata[1] = DisplayHeight(dpy, 0);
- tvdata[2] = tvdata[0];
- tvdata[3] = tvdata[1];
- break;
- case TV_ABOVE_BELOW:
- case TV_BELOW_ABOVE:
- tvdata[0] = DisplayWidth(dpy, 0);
- tvdata[1] = DisplayHeight(dpy, 0)/2;
- tvdata[2] = tvdata[0];
- tvdata[3] = tvdata[1];
- break;
+ *return_value = (int)conversion;
}
- XChangeDeviceProperty(dpy, dev, prop, type, format,
- PropModeReplace, data, nitems);
-
- XFlush(dpy);
-
- return;
-
-error:
- fprintf(stderr, "Usage: xsetwacom <device name> TwinView [<value>]\n");
- fprintf(stderr, "Values: none, horizontal, vertical, belowof,"
- "aboveof, leftof, rightof\n");
- return;
-}
-
-static int convert_value_from_user(param_t *param, char *value)
-{
- int val;
-
- if ((param->prop_flags & PROP_FLAG_BOOLEAN) && strcmp(value, "off") == 0)
- val = 0;
- else if ((param->prop_flags & PROP_FLAG_BOOLEAN) && strcmp(value, "on") == 0)
- val = 1;
- else
- val = atoi(value);
-
- return val;
+ return True;
}
static void set(Display *dpy, int argc, char **argv)
{
param_t *param;
XDevice *dev = NULL;
- Atom prop, type;
+ Atom prop = None, type;
int format;
unsigned char* data = NULL;
unsigned long nitems, bytes_after;
- double val;
long *n;
char *b;
int i;
char **values;
int nvals;
- if (argc < 3)
+ if (argc < 2)
{
usage();
return;
@@ -1798,23 +1553,30 @@ static void set(Display *dpy, int argc, char **argv)
param = find_parameter(argv[1]);
if (!param)
{
+ if (is_deprecated_parameter(argv[1]))
+ goto out;
printf("Unknown parameter name '%s'.\n", argv[1]);
goto out;
} else if (param->prop_flags & PROP_FLAG_READONLY)
{
printf("'%s' is a read-only option.\n", argv[1]);
goto out;
- } else if (param->set_func)
+ }
+
+ if (param->prop_name)
{
- param->set_func(dpy, dev, param, argc - 2, &argv[2]);
- goto out;
+ prop = XInternAtom(dpy, param->prop_name, True);
+ if (!prop || !test_property(dpy, dev, prop))
+ {
+ printf("Property '%s' does not exist on device.\n",
+ param->prop_name);
+ goto out;
+ }
}
- prop = XInternAtom(dpy, param->prop_name, True);
- if (!prop)
+ if (param->set_func)
{
- fprintf(stderr, "Property for '%s' not available.\n",
- param->name);
+ param->set_func(dpy, dev, param, argc - 2, &argv[2]);
goto out;
}
@@ -1829,9 +1591,25 @@ static void set(Display *dpy, int argc, char **argv)
values = strjoinsplit(argc - 2, &argv[2], &nvals);
+ if (nvals != param->arg_count)
+ {
+ fprintf(stderr, "'%s' requires exactly %d value(s).\n", param->name,
+ param->arg_count);
+ goto out;
+ }
+
for (i = 0; i < nvals; i++)
{
- val = convert_value_from_user(param, values[i]);
+ Bool success;
+ int val;
+
+ success = convert_value_from_user(param, values[i], &val);
+ if (!success)
+ {
+ fprintf(stderr, "'%s' is not a valid value for the '%s' property.\n",
+ values[i], param->name);
+ goto out;
+ }
switch(param->prop_format)
{
@@ -1883,7 +1661,10 @@ static void get_mode(Display *dpy, XDevice *dev, param_t* param, int argc, char
}
if (!ndevices) /* device id 0 is reserved and can't be our device */
+ {
+ fprintf(stderr, "Unable to locate device.\n");
return;
+ }
TRACE("Getting mode for device %ld.\n", dev->device_id);
@@ -1909,6 +1690,12 @@ static void get_rotate(Display *dpy, XDevice *dev, param_t* param, int argc, cha
unsigned char* data;
unsigned long nitems, bytes_after;
+ if (argc != 0)
+ {
+ fprintf(stderr, "Incorrect number of arguments supplied.\n");
+ return;
+ }
+
prop = XInternAtom(dpy, param->prop_name, True);
if (!prop)
{
@@ -1932,16 +1719,16 @@ static void get_rotate(Display *dpy, XDevice *dev, param_t* param, int argc, cha
switch(*data)
{
case 0:
- rotation = "NONE";
+ rotation = "none";
break;
case 1:
- rotation = "CW";
+ rotation = "cw";
break;
case 2:
- rotation = "CCW";
+ rotation = "ccw";
break;
case 3:
- rotation = "HALF";
+ rotation = "half";
break;
}
@@ -1950,133 +1737,61 @@ static void get_rotate(Display *dpy, XDevice *dev, param_t* param, int argc, cha
return;
}
-static void get_twinview(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv)
+/**
+ * Try to print the value of the action mapped to the given parameter's
+ * property. If the property contains data in the wrong format/type then
+ * nothing will be printed.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ * @param param Info about parameter to query
+ * @param offset Offset into property specified in param
+ * @return 0 on failure, 1 otherwise
+ */
+static int get_actions(Display *dpy, XDevice *dev,
+ param_t *param, int offset)
{
- char *twinview = NULL;
Atom prop, type;
int format;
- unsigned char* data;
- unsigned long nitems, bytes_after;
-
- prop = XInternAtom(dpy, param->prop_name, True);
- if (!prop)
- {
- fprintf(stderr, "Property for '%s' not available.\n",
- param->name);
- return;
- }
-
- TRACE("Getting twinview setting for device %ld.\n", dev->device_id);
-
- XGetDeviceProperty(dpy, dev, prop, 0, 1000, False, AnyPropertyType,
- &type, &format, &nitems, &bytes_after, &data);
-
- if (nitems == 0 || format != 8)
- {
- fprintf(stderr, "Property for '%s' has no or wrong value - this is a bug.\n",
- param->name);
- return;
- }
-
- switch(data[param->prop_offset])
- {
- case TV_NONE: twinview = "none"; break;
- case TV_ABOVE_BELOW: twinview = "vertical"; break;
- case TV_LEFT_RIGHT: twinview = "horizontal"; break;
- case TV_RIGHT_LEFT: twinview = "leftof"; break;
- case TV_BELOW_ABOVE: twinview = "aboveof"; break;
- default:
- break;
- }
-
- print_value(param, "%s", twinview);
-
- return;
-}
-
-static void get_presscurve(Display *dpy, XDevice *dev, param_t *param, int argc,
- char **argv)
-{
- Atom prop, type;
- int format, i;
- unsigned char* data;
- unsigned long nitems, bytes_after;
- char buff[256] = {0};
- long *ldata;
-
- prop = XInternAtom(dpy, param->prop_name, True);
- if (!prop)
- {
- fprintf(stderr, "Property for '%s' not available.\n",
- param->name);
- return;
- }
-
- TRACE("Getting pressure curve for device %ld.\n", dev->device_id);
-
- XGetDeviceProperty(dpy, dev, prop, 0, 1000, False, AnyPropertyType,
- &type, &format, &nitems, &bytes_after, &data);
-
- if (param->prop_format != 32)
- return;
-
- ldata = (long*)data;
- if (nitems)
- sprintf(buff, "%ld", ldata[param->prop_offset]);
- for (i = 1; i < nitems; i++)
- sprintf(&buff[strlen(buff)], " %ld", ldata[param->prop_offset + i]);
-
- print_value(param, "%s", buff);
-}
-
-static int get_special_button_map(Display *dpy, XDevice *dev,
- param_t *param, int btn_no)
-{
- Atom btnact_prop, action_prop;
- unsigned long *btnact_data;
- Atom type;
- int format;
- unsigned long btnact_nitems, bytes_after;
+ unsigned long nitems, bytes_after, *data;
int i;
char buff[1024] = {0};
- btnact_prop = XInternAtom(dpy, "Wacom Button Actions", True);
+ prop = XInternAtom(dpy, param->prop_name, True);
- if (!btnact_prop)
+ if (!prop)
return 0;
- XGetDeviceProperty(dpy, dev, btnact_prop, 0, 100, False,
- AnyPropertyType, &type, &format, &btnact_nitems,
- &bytes_after, (unsigned char**)&btnact_data);
+ XGetDeviceProperty(dpy, dev, prop, 0, 100, False,
+ AnyPropertyType, &type, &format, &nitems,
+ &bytes_after, (unsigned char**)&data);
- /* button numbers start at 1, property is zero-indexed */
- if (btn_no >= btnact_nitems)
+ if (offset >= nitems)
+ {
+ XFree(data);
return 0;
+ }
- /* FIXME: doesn't cover wheels/strips at the moment, they can be 8
- * bits (plain buttons) or 32 bits (complex actions) */
+ prop = data[offset];
+ XFree(data);
- action_prop = btnact_data[btn_no - 1];
- if (!action_prop)
+ if (format != 32 || type != XA_ATOM || !prop)
+ {
return 0;
+ }
- XFree(btnact_data);
-
- XGetDeviceProperty(dpy, dev, action_prop, 0, 100, False,
- AnyPropertyType, &type, &format, &btnact_nitems,
- &bytes_after, (unsigned char**)&btnact_data);
+ XGetDeviceProperty(dpy, dev, prop, 0, 100, False,
+ AnyPropertyType, &type, &format, &nitems,
+ &bytes_after, (unsigned char**)&data);
- if (format != 32 && type != XA_ATOM)
- return 0;
-
- for (i = 0; i < btnact_nitems; i++)
+ for (i = 0; i < nitems; i++)
{
- static int last_type, last_press;
- unsigned long action = btnact_data[i];
+ static int last_type;
+ unsigned long action = data[i];
int current_type;
int detail;
int is_press = -1;
- char str[10] = {0};
+ char str[32] = {0};
char press_str = ' ';
current_type = action & AC_TYPE;
@@ -2115,56 +1830,557 @@ static int get_special_button_map(Display *dpy, XDevice *dev,
sprintf(str, "%c%d ", press_str, detail);
strcat(buff, str);
last_type = current_type;
- last_press = is_press;
}
TRACE("%s\n", buff);
- XFree(btnact_data);
+ XFree(data);
print_value(param, "%s", buff);
return 1;
}
-static void get_button(Display *dpy, XDevice *dev, param_t *param, int argc,
- char **argv)
+/**
+ * Try to print the value of the raw button mapped to the given parameter's
+ * property. If the property contains data in the wrong format/type then
+ * nothing will be printed.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ * @param param Info about parameter to query
+ * @param offset Offset into the property specified in param
+ * @return 0 on failure, 1 otherwise
+ */
+static int get_button(Display *dpy, XDevice *dev, param_t *param, int offset)
+{
+ Atom prop, type;
+ int format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+
+ prop = XInternAtom(dpy, param->prop_name, True);
+
+ if (!prop)
+ return 0;
+
+ XGetDeviceProperty(dpy, dev, prop, 0, 100, False,
+ AnyPropertyType, &type, &format, &nitems,
+ &bytes_after, (unsigned char**)&data);
+
+ if (offset >= nitems)
+ {
+ XFree(data);
+ return 0;
+ }
+
+ prop = data[offset];
+ XFree(data);
+
+ if (format != 8 || type != XA_INTEGER || !prop)
+ {
+ return 0;
+ }
+
+ print_value(param, "%d", prop);
+
+ return 1;
+}
+
+/**
+ * Print the current button/wheel/strip mapping, be it a raw button or
+ * an action. Button map requests require the button number as the first
+ * argument in argv.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ * @param param Info about parameter to query
+ * @param argc Length of argv
+ * @param argv Command-line arguments
+ */
+static void get_map(Display *dpy, XDevice *dev, param_t *param, int argc, char** argv)
{
- int nmap = 256;
- unsigned char map[nmap];
- int btn_no = 0;
+ int offset = param->prop_offset;
+
+ TRACE("Getting button map for device %ld.\n", dev->device_id);
- btn_no = get_button_number_from_string(param->name);
- if (btn_no == -1)
+ if (argc != param->arg_count)
+ {
+ fprintf(stderr, "'%s' requires exactly %d value(s).\n", param->name,
+ param->arg_count);
return;
+ }
- TRACE("Getting button map curve for device %ld.\n", dev->device_id);
+ if (strcmp(param->prop_name, WACOM_PROP_BUTTON_ACTIONS) == 0)
+ {
+ if (sscanf(argv[0], "%d", &offset) != 1)
+ {
+ fprintf(stderr, "'%s' is not a valid button number.\n", argv[0]);
+ return;
+ }
- /* if there's a special map, print it and return */
- if (get_special_button_map(dpy, dev, param, btn_no))
+ offset--; /* Property is 0-indexed, X buttons are 1-indexed */
+ argc--; /* Trim off the target button argument */
+ argv = &argv[1]; /*... ditto ... */
+ }
+
+
+ if (get_actions(dpy, dev, param, offset))
+ return;
+ else if (get_button(dpy, dev, param, offset))
return;
+ else
+ {
+ int nmap = 256;
+ unsigned char map[nmap];
+
+ nmap = XGetDeviceButtonMapping(dpy, dev, map, nmap);
+
+ if (offset >= nmap)
+ {
+ fprintf(stderr, "Button number does not exist on device.\n");
+ return;
+ }
+
+ print_value(param, "%d", map[offset]);
+
+ XSetDeviceButtonMapping(dpy, dev, map, nmap);
+ XFlush(dpy);
+ }
+}
+
+/**
+ * Determine if we need to use fall back to Xinerama, or if the RandR
+ * extension will work OK. We depend on RandR 1.3 or better in order
+ * to work.
+ *
+ * A server bug causes the NVIDIA driver to report RandR 1.3 support
+ * despite not exposing RandR CRTCs. We need to fall back to Xinerama
+ * for this case as well.
+ *
+ * @param Display X11 display to connect to
+ * @return True if the Xinerama should be used instead of RandR
+ */
+static Bool need_xinerama(Display *dpy)
+{
+ int opcode, event, error;
+ int maj, min;
+
+ if (!XQueryExtension(dpy, "RANDR", &opcode, &event, &error) ||
+ !XRRQueryVersion(dpy, &maj, &min) || (maj * 1000 + min) < 1002 ||
+ XQueryExtension(dpy, "NV-CONTROL", &opcode, &event, &error))
+ {
+ TRACE("RandR extension not found, too old, or NV-CONTROL "
+ "extension is also present.\n");
+ return True;
+ }
- nmap = XGetDeviceButtonMapping(dpy, dev, map, nmap);
+ return False;
+}
- if (btn_no > nmap)
+/**
+ * Uses the area of the desktop and the server's transformation
+ * matrix to calculate the dimensions and location of the area
+ * the given device is mapped to. If the matrix describes a
+ * non-rectangular transform (e.g. rotation or shear), this
+ * function returns False.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ * @param width[out] Width of the mapped area
+ * @param height[out] Height of the mapped area
+ * @param x_org[out] Offset from the desktop origin to the mapped area's left edge
+ * @param y_org[out] Offset from the desktop origin to the mapped area's top edge
+ * @return True if the function could determine the mapped area
+ */
+Bool get_mapped_area(Display *dpy, XDevice *dev, int *width, int *height, int *x_org, int *y_org)
+{
+ Atom matrix_prop = XInternAtom(dpy, "Coordinate Transformation Matrix", True);
+ Atom type;
+ int format;
+ unsigned long nitems, bytes_after;
+ float *data;
+ Bool matrix_is_valid = True;
+ int i;
+
+ int display_width = DisplayWidth(dpy, DefaultScreen(dpy));
+ int display_height = DisplayHeight(dpy, DefaultScreen(dpy));
+ TRACE("Desktop width: %d, height: %d\n", display_width, display_height);
+
+ if (!matrix_prop)
{
- fprintf(stderr, "Button number does not exist on device.\n");
- return;
+ fprintf(stderr, "Server does not support transformation\n");
+ return False;
+ }
+
+ XGetDeviceProperty(dpy, dev, matrix_prop, 0, 9, False,
+ AnyPropertyType, &type, &format, &nitems,
+ &bytes_after, (unsigned char**)&data);
+
+ if (format != 32 || type != XInternAtom(dpy, "FLOAT", True))
+ {
+ fprintf(stderr,"Property for '%s' has unexpected type - this is a bug.\n",
+ "Coordinate Transformation Matrix");
+ XFree(data);
+ return False;
+ }
+
+ TRACE("Current transformation matrix:\n");
+ TRACE(" [ %f %f %f ]\n", data[0], data[1], data[2]);
+ TRACE(" [ %f %f %f ]\n", data[3], data[4], data[5]);
+ TRACE(" [ %f %f %f ]\n", data[6], data[7], data[8]);
+
+ for (i = 0; i < nitems && matrix_is_valid; i++)
+ {
+ switch (i) {
+ case 0: *width = rint(display_width * data[i]); break;
+ case 2: *x_org = rint(display_width * data[i]); break;
+ case 4: *height = rint(display_height * data[i]); break;
+ case 5: *y_org = rint(display_height * data[i]); break;
+ case 8:
+ if (data[i] != 1)
+ matrix_is_valid = False;
+ break;
+ default:
+ if (data[i] != 0)
+ matrix_is_valid = False;
+ break;
+ }
+ }
+ XFree(data);
+
+ if (!matrix_is_valid)
+ fprintf(stderr, "Non-rectangular transformation matrix detected.\n");
+
+ return matrix_is_valid;
+}
+
+/**
+ * Modifies the server's transformation matrix property for the given
+ * device. It takes as input a 9-element array of floats interpreted
+ * as the row-major 3x3 matrix to be set.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ * @param fmatrix A row-major 3x3 transformation matrix
+ * @return True if the transformation matrix was successfully modified
+ */
+static Bool _set_matrix_prop(Display *dpy, XDevice *dev, const float fmatrix[9])
+{
+ Atom matrix_prop = XInternAtom(dpy, "Coordinate Transformation Matrix", True);
+ Atom type;
+ int format;
+ unsigned long nitems, bytes_after;
+ float *data;
+ long matrix[9] = {0};
+ int i;
+
+ if (!matrix_prop)
+ {
+ fprintf(stderr, "Server does not support transformation\n");
+ return False;
}
- print_value(param, "%d", map[btn_no - 1]);
+ /* XI1 expects 32 bit properties (including float) as long,
+ * regardless of architecture */
+ for (i = 0; i < ARRAY_SIZE(matrix); i++)
+ *(float*)(matrix + i) = fmatrix[i];
+
+ XGetDeviceProperty(dpy, dev, matrix_prop, 0, 9, False,
+ AnyPropertyType, &type, &format, &nitems,
+ &bytes_after, (unsigned char**)&data);
- XSetDeviceButtonMapping(dpy, dev, map, nmap);
+ if (format != 32 || type != XInternAtom(dpy, "FLOAT", True))
+ {
+ fprintf(stderr, "Property for '%s' has unexpected type - this is a bug.\n",
+ "Coordinate Transformation Matrix");
+ return False;
+ }
+
+ XChangeDeviceProperty(dpy, dev, matrix_prop, type, format,
+ PropModeReplace, (unsigned char*)matrix, 9);
+ XFree(data);
XFlush(dpy);
+
+ return True;
+}
+
+/**
+ * Adjust the transformation matrix based on a user-defined area.
+ * This function will attempt to map the given pointer to an arbitrary
+ * rectangular portion of the desktop.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ * @param offset_x Offset of output area's left edge from desktop origin
+ * @param offset_y Offset of output area's top edge from desktop origin
+ * @param output_width Width of output area
+ * @param output_height Height of output area
+ * @return True if the transformation matrix was successfully modified
+ */
+static Bool set_output_area(Display *dpy, XDevice *dev,
+ int offset_x, int offset_y,
+ int output_width, int output_height)
+{
+ int width = DisplayWidth(dpy, DefaultScreen(dpy));
+ int height = DisplayHeight(dpy, DefaultScreen(dpy));
+
+ /* offset */
+ float x = 1.0 * offset_x/width;
+ float y = 1.0 * offset_y/height;
+
+ /* mapping */
+ float w = 1.0 * output_width/width;
+ float h = 1.0 * output_height/height;
+
+ float matrix[9] = { 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1};
+ matrix[2] = x;
+ matrix[5] = y;
+ matrix[0] = w;
+ matrix[4] = h;
+
+ TRACE("Remapping to output area %dx%d @ %d,%d.\n", output_width,
+ output_height, offset_x, offset_y);
+
+ TRACE("Transformation matrix:\n");
+ TRACE(" [ %f %f %f ]\n", matrix[0], matrix[1], matrix[2]);
+ TRACE(" [ %f %f %f ]\n", matrix[3], matrix[4], matrix[5]);
+ TRACE(" [ %f %f %f ]\n", matrix[6], matrix[7], matrix[8]);
+
+ return _set_matrix_prop(dpy, dev, matrix);
+}
+
+
+/**
+ * Adjust the transformation matrix based on RandR settings. This function
+ * will attempt to map the given device to the output with the given RandR
+ * output name.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ * @param output_name Name of the RandR output to map to
+ * @return True if the transformation matrix was successfully modified
+ */
+static Bool set_output_xrandr(Display *dpy, XDevice *dev, char *output_name)
+{
+ int i, found = 0;
+ int x, y, width, height;
+ XRRScreenResources *res;
+ XRROutputInfo *output_info;
+ XRRCrtcInfo *crtc_info;
+
+ res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
+ for (i = 0; i < res->noutput && !found; i++)
+ {
+ output_info = XRRGetOutputInfo(dpy, res, res->outputs[i]);
+
+ TRACE("Found output '%s' (%s)\n", output_info->name,
+ output_info->connection == RR_Connected ? "connected" : "disconnnected");
+
+ if (!output_info->crtc || output_info->connection != RR_Connected)
+ continue;
+
+ crtc_info = XRRGetCrtcInfo (dpy, res, output_info->crtc);
+ x = crtc_info->x;
+ y = crtc_info->y;
+ width = crtc_info->width;
+ height = crtc_info->height;
+ XRRFreeCrtcInfo(crtc_info);
+
+ TRACE("CRTC (%dx%d) %dx%d\n", x, y, width, height);
+
+ if (strcmp(output_info->name, output_name) == 0)
+ {
+ found = 1;
+ break;
+ }
+ }
+ XRRFreeScreenResources(res);
+
+ /* crtc holds our screen info, need to compare to actual screen size */
+ if (found)
+ {
+ TRACE("Setting CRTC %s\n", output_name);
+ return set_output_area(dpy, dev, x, y, width, height);
+ } else
+ {
+ printf("Unable to find output '%s'. "
+ "Output may not be connected.\n", output_name);
+
+ return False;
+ }
}
+/**
+ * Adjust the transformation matrix based on the Xinerama settings. This
+ * function will attempt to map the given device to the specified Xinerama
+ * head number.
+ *
+ * For TwinView This would better be done with libXNVCtrl but until they
+ * learn to package it properly, we need to rely on Xinerama. Besides,
+ * libXNVCtrl isn't available on RHEL, so we'd have to do it through
+ * Xinerama there anyway.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ * @param head Index of Xinerama head to map to
+ * @return True if the transformation matrix was successfully modified
+ */
+static Bool set_output_xinerama(Display *dpy, XDevice *dev, int head)
+{
+ int event, error;
+ XineramaScreenInfo *screens;
+ int nscreens;
+ Bool success = False;
+
+ if (!XineramaQueryExtension(dpy, &event, &error))
+ {
+ fprintf(stderr, "Unable to set screen mapping. Xinerama extension not found\n");
+ return success;
+ }
+
+ screens = XineramaQueryScreens(dpy, &nscreens);
+
+ if (nscreens == 0)
+ {
+ fprintf(stderr, "Xinerama failed to query screens.\n");
+ goto out;
+ } else if (nscreens <= head)
+ {
+ fprintf(stderr, "Found %d screens, but you requested number %d.\n",
+ nscreens, head);
+ goto out;
+ }
+
+ TRACE("Setting xinerama head %d\n", head);
+
+ success = set_output_area(dpy, dev,
+ screens[head].x_org, screens[head].y_org,
+ screens[head].width, screens[head].height);
+
+out:
+ XFree(screens);
+ return success;
+}
+
+/**
+ * Adjust the transformation matrix based on the desktop size.
+ * This function will attempt to map the given device to the entire
+ * desktop.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ * @return True if the transformation matrix was successfully modified
+ */
+static Bool set_output_desktop(Display *dpy, XDevice *dev)
+{
+ int display_width = DisplayWidth(dpy, DefaultScreen(dpy));
+ int display_height = DisplayHeight(dpy, DefaultScreen(dpy));
+
+ return set_output_area(dpy, dev, 0, 0, display_width, display_height);
+}
+
+/**
+ * Adjust the transformation matrix based on its current value. This
+ * function will attempt to map the given device to the next output
+ * exposed in the list of Xinerama heads. If not mapped to a Xinerama
+ * head, it maps to the first head. If mapped to the last Xinerama
+ * head, it maps to the entire desktop.
+ *
+ * @param dpy X11 display to connect to
+ * @param dev Device to query
+ * @return True if the transformation matrix was successfully modified
+ */
+static Bool set_output_next(Display *dpy, XDevice *dev)
+{
+ XineramaScreenInfo *screens;
+ int event, error, nscreens, head;
+ int width, height, x_org, y_org;
+ Bool success = False;
+
+ if (!get_mapped_area(dpy, dev, &width, &height, &x_org, &y_org))
+ return success;
+
+ if (!XineramaQueryExtension(dpy, &event, &error))
+ {
+ fprintf(stderr, "Unable to get screen mapping. Xinerama extension not found\n");
+ return success;
+ }
+
+ screens = XineramaQueryScreens(dpy, &nscreens);
+
+ if (nscreens == 0)
+ {
+ fprintf(stderr, "Xinerama failed to query screens.\n");
+ goto out;
+ }
+
+ TRACE("Remapping to next available output.\n");
+ for (head = 0; head < nscreens && !success; head++)
+ {
+ if (screens[head].width == width && screens[head].height == height &&
+ screens[head].x_org == x_org && screens[head].y_org == y_org)
+ {
+ if (head + 1 < nscreens)
+ success = set_output_xinerama(dpy, dev, head+1);
+ else
+ success = set_output_desktop(dpy, dev);
+
+ if (!success)
+ goto out;
+ }
+ }
+
+ if (!success)
+ success = set_output_xinerama(dpy, dev, 0);
+
+out:
+ XFree(screens);
+ return success;
+}
+
+static void set_output(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv)
+{
+ int head_no;
+ int x, y;
+ unsigned int width, height;
+ int flags = XParseGeometry(argv[0], &x, &y, &width, &height);
+ Bool success = False;
+
+ if (argc != param->arg_count)
+ {
+ fprintf(stderr, "'%s' requires exactly %d value(s).\n", param->name,
+ param->arg_count);
+ return;
+ }
+
+ if (MaskIsSet(flags, XValue|YValue|WidthValue|HeightValue))
+ success = set_output_area(dpy, dev, x, y, width, height);
+ else if (strcasecmp(argv[0], "next") == 0)
+ success = set_output_next(dpy, dev);
+ else if (strcasecmp(argv[0], "desktop") == 0)
+ success = set_output_desktop(dpy, dev);
+ else if (!need_xinerama(dpy))
+ success = set_output_xrandr(dpy, dev, argv[0]);
+ else if (convert_value_from_user(param, argv[0], &head_no))
+ success = set_output_xinerama(dpy, dev, head_no);
+ else
+ fprintf(stderr, "Unable to find an output '%s'.\n", argv[0]);
+}
+
+
static void get_all(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv)
{
param_t *p = parameters;
+ if (param->printformat == FORMAT_DEFAULT)
+ param->printformat = FORMAT_XORG_CONF;
+
while(p->name)
{
- if (p != param)
+ if (p != param && !(p->prop_flags & PROP_FLAG_WRITEONLY))
{
p->device_name = param->device_name;
p->printformat = param->printformat;
@@ -2197,6 +2413,8 @@ static void get(Display *dpy, enum printformat printformat, int argc, char **arg
param = find_parameter(argv[1]);
if (!param)
{
+ if (is_deprecated_parameter(argv[1]))
+ return;
printf("Unknown parameter name '%s'.\n", argv[1]);
return;
} else if (param->prop_flags & PROP_FLAG_WRITEONLY)
@@ -2216,27 +2434,33 @@ static void get(Display *dpy, enum printformat printformat, int argc, char **arg
static void get_param(Display *dpy, XDevice *dev, param_t *param, int argc, char **argv)
{
- Atom prop, type;
+ Atom prop = None, type;
int format;
unsigned char* data;
unsigned long nitems, bytes_after;
int i;
char str[100] = {0};
- if (param->get_func)
+ if (param->prop_name)
{
- param->get_func(dpy, dev, param, argc, argv);
- return;
+ prop = XInternAtom(dpy, param->prop_name, True);
+ if (!prop || !test_property(dpy, dev, prop))
+ {
+ printf("Property '%s' does not exist on device.\n",
+ param->prop_name);
+ return;
+ }
}
- prop = XInternAtom(dpy, param->prop_name, True);
- if (!prop)
+ if (param->get_func)
{
- fprintf(stderr, "Property for '%s' not available.\n",
- param->name);
+ TRACE("custom get func for param\n");
+ param->get_func(dpy, dev, param, argc, argv);
return;
}
+
+ TRACE("Getting property %ld, offset %d\n", prop, param->prop_offset);
XGetDeviceProperty(dpy, dev, prop, 0, 1000, False, AnyPropertyType,
&type, &format, &nitems, &bytes_after, &data);
@@ -2250,27 +2474,30 @@ static void get_param(Display *dpy, XDevice *dev, param_t *param, int argc, char
switch(param->prop_format)
{
case 8:
- for (i = 0; i < 1 + param->prop_extra; i++)
+ for (i = 0; i < param->arg_count; i++)
{
int val = data[param->prop_offset + i];
if (param->prop_flags & PROP_FLAG_BOOLEAN)
- sprintf(&str[strlen(str)], "%s", val ? "on" : "off");
+ if (param->prop_flags & PROP_FLAG_INVERTED)
+ sprintf(&str[strlen(str)], "%s", val ? "off" : "on");
+ else
+ sprintf(&str[strlen(str)], "%s", val ? "on" : "off");
else
sprintf(&str[strlen(str)], "%d", val);
- if (i < param->prop_extra)
+ if (i < param->arg_count - 1)
strcat(str, " ");
}
print_value(param, "%s", str);
break;
case 32:
- for (i = 0; i < 1 + param->prop_extra; i++)
+ for (i = 0; i < param->arg_count; i++)
{
long *ldata = (long*)data;
sprintf(&str[strlen(str)], "%ld", ldata[param->prop_offset + i]);
- if (i < param->prop_extra)
+ if (i < param->arg_count - 1)
strcat(str, " ");
}
print_value(param, "%s", str);
@@ -2279,9 +2506,10 @@ static void get_param(Display *dpy, XDevice *dev, param_t *param, int argc, char
}
+#ifndef BUILD_TEST
int main (int argc, char **argv)
{
- char c;
+ int c;
int optidx;
char *display = NULL;
Display *dpy;
@@ -2313,9 +2541,9 @@ int main (int argc, char **argv)
case 0:
switch(optidx)
{
- case 0: usage(); break;
+ case 0: usage(); return 0;
case 1: verbose = True; break;
- case 2: version(); break;
+ case 2: version(); return 0;
case 3: /* display */
break;
case 4:
@@ -2343,7 +2571,7 @@ int main (int argc, char **argv)
break;
case 'V':
version();
- break;
+ return 0;
case 'h':
default:
usage();
@@ -2393,6 +2621,161 @@ int main (int argc, char **argv)
XCloseDisplay(dpy);
return 0;
}
+#endif
+
+#ifdef BUILD_TEST
+#include <assert.h>
+/**
+ * Below are unit-tests to ensure xsetwacom continues to work as expected.
+ */
+static void test_is_modifier(void)
+{
+ char i;
+ char buff[5];
+
+
+ assert(is_modifier("Control_L"));
+ assert(is_modifier("Control_R"));
+ assert(is_modifier("Alt_L"));
+ assert(is_modifier("Alt_R"));
+ assert(is_modifier("Shift_L"));
+ assert(is_modifier("Shift_R"));
+ assert(is_modifier("Meta_L"));
+ assert(is_modifier("Meta_R"));
+ assert(is_modifier("Super_L"));
+ assert(is_modifier("Super_R"));
+ assert(is_modifier("Hyper_L"));
+ assert(is_modifier("Hyper_R"));
+
+ assert(!is_modifier(""));
+
+ /* make sure at least the default keys (ascii 33 - 126) aren't
+ * modifiers */
+ for (i = '!'; i <= '~'; i++)
+ {
+ sprintf(buff, "%c", i);
+ assert(!is_modifier(buff));
+ }
+}
+
+static void test_convert_specialkey(void)
+{
+ char i;
+ char *converted;
+ char buff[5];
+ struct modifier *m;
+
+ /* make sure at least the default keys (ascii 33 - 126) aren't
+ * specialkeys */
+ for (i = '!'; i <= '~'; i++)
+ {
+ sprintf(buff, "%c", i);
+ converted = convert_specialkey(buff);
+ assert(strcmp(converted, buff) == 0);
+ }
+
+ for (m = specialkeys; m->name; m++)
+ {
+ converted = convert_specialkey(m->name);
+ assert(strcmp(converted, m->converted) == 0);
+ }
+}
+
+static void test_parameter_number(void)
+{
+ /* If either of those two fails, a parameter was added or removed.
+ * This test simply exists so that we remember to properly
+ * deprecated them.
+ * Numbers include trailing NULL entry.
+ */
+ assert(ARRAY_SIZE(parameters) == 36);
+ assert(ARRAY_SIZE(deprecated_parameters) == 17);
+}
+
+/**
+ * For the given parameter, test all words against conversion success and
+ * expected value.
+ *
+ * @param param The parameter of type PROP_FLAG_BOOLEAN.
+ * @param words NULL-terminated word list to parse
+ * @param success True if conversion success for the words is expected or
+ * False overwise
+ * @param expected Expected converted value. If success is False, this value
+ * is omitted.
+ */
+static void _test_conversion(const param_t *param, const char **words,
+ Bool success, Bool expected)
+{
-/* vim: set noexpandtab shiftwidth=8: */
+ assert(param->prop_flags & PROP_FLAG_BOOLEAN);
+
+ while(*words)
+ {
+ int val;
+ int rc;
+ rc = convert_value_from_user(param, *words, &val);
+ assert(rc == success);
+ if (success)
+ assert(val == expected);
+ words++;
+ }
+}
+
+static void test_convert_value_from_user(void)
+{
+ param_t test_nonbool =
+ {
+ .name = "Test",
+ .desc = "NOT A REAL PARAMETER",
+ .prop_flags = 0,
+ };
+
+ param_t test_bool =
+ {
+ .name = "Test",
+ .desc = "NOT A REAL PARAMETER",
+ .prop_flags = PROP_FLAG_BOOLEAN,
+ };
+
+ param_t test_bool_inverted =
+ {
+ .name = "Test",
+ .desc = "NOT A REAL PARAMETER",
+ .prop_flags = PROP_FLAG_BOOLEAN | PROP_FLAG_INVERTED,
+ };
+
+ const char *bool_true[] = { "true", "TRUE", "True", "On", "on", "ON", NULL };
+ const char *bool_false[] = { "false", "FALSE", "False", "Off", "off", "OFF", NULL };
+ const char *bool_garbage[] = { "0", "1", " on", "on ", " off", " off", NULL};
+
+ int val;
+
+ assert(convert_value_from_user(&test_nonbool, "1", &val) == True);
+ assert(convert_value_from_user(&test_nonbool, "-8", &val) == True);
+ assert(convert_value_from_user(&test_nonbool, "+314", &val) == True);
+ assert(convert_value_from_user(&test_nonbool, "36893488147419103232", &val) == False); //2^65 > MAX_INT
+ assert(convert_value_from_user(&test_nonbool, "123abc", &val) == False);
+ assert(convert_value_from_user(&test_nonbool, "123 abc", &val) == False);
+
+ _test_conversion(&test_bool, bool_true, True, True);
+ _test_conversion(&test_bool, bool_false, True, False);
+ _test_conversion(&test_bool, bool_garbage, False, False);
+
+ _test_conversion(&test_bool_inverted, bool_true, True, False);
+ _test_conversion(&test_bool_inverted, bool_false, True, True);
+ _test_conversion(&test_bool_inverted, bool_garbage, False, False);
+}
+
+
+int main(int argc, char** argv)
+{
+ test_parameter_number();
+ test_is_modifier();
+ test_convert_specialkey();
+ test_convert_value_from_user();
+ return 0;
+}
+
+#endif
+/* vim: set noexpandtab tabstop=8 shiftwidth=8: */